Skip to content

[Wheel] Gracefully stop Store REST service on SIGTERM#2874

Merged
ykwd merged 2 commits into
kvcache-ai:mainfrom
zhangzuo21:port/store-sigterm-shutdown
Jul 17, 2026
Merged

[Wheel] Gracefully stop Store REST service on SIGTERM#2874
ykwd merged 2 commits into
kvcache-ai:mainfrom
zhangzuo21:port/store-sigterm-shutdown

Conversation

@zhangzuo21

@zhangzuo21 zhangzuo21 commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Description

The Store REST service currently relies on KeyboardInterrupt around a polling loop for shutdown. Process managers commonly terminate services with SIGTERM, and a shutdown request received during startup can remain pending while the service is retrying or proceed into HTTP startup before the asyncio signal callback runs. Cleanup is also duplicated across exception paths instead of being guaranteed for every exit path.

This PR:

  • installs asyncio handlers for SIGINT and SIGTERM and unblocks inherited signal masks;
  • uses an asyncio.Event to drive the service lifetime;
  • makes startup retry waits interruptible by a shutdown request;
  • observes queued shutdown callbacks before and immediately after synchronous Store setup, preventing HTTP startup after termination was requested;
  • closes the Store from a finally block and reports nonzero close results.

MooncakeDistributedStore.setup() remains synchronous and is not cancelled while it is executing. A pending shutdown request is honored as soon as setup returns.

Module

  • Transfer Engine (mooncake-transfer-engine)
  • Mooncake Store (mooncake-store)
  • Mooncake EP (mooncake-ep)
  • Mooncake PG (mooncake-pg)
  • Integration (mooncake-integration)
  • P2P Store (mooncake-p2p-store)
  • Python Wheel (mooncake-wheel)
  • Common (mooncake-common)
  • Mooncake RL (mooncake-rl)
  • CI/CD
  • Docs
  • Other

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation update
  • Performance improvement
  • Other

How Has This Been Tested?

The shutdown tests cover signal-handler dispatch, cancellation before and after Store setup, interruption of retry sleeps, guaranteed cleanup from main(), and nonzero close results.

Test commands:

PYTHONDONTWRITEBYTECODE=1 python3 \
  mooncake-wheel/tests/test_mooncake_store_service_api.py \
  StoreServiceShutdownTest

uvx --from ruff==0.6.9 ruff check \
  mooncake-wheel/mooncake/mooncake_store_service.py \
  mooncake-wheel/tests/test_mooncake_store_service_api.py

uvx --from ruff==0.6.9 ruff format --check \
  mooncake-wheel/mooncake/mooncake_store_service.py \
  mooncake-wheel/tests/test_mooncake_store_service_api.py

git diff --check upstream/main...HEAD

Test results:

  • Unit tests pass (5 shutdown tests)
  • Integration tests pass (not run)
  • Manual testing done

Checklist

  • I have performed a self-review of my own code
  • I have formatted my code using ./scripts/code_format.sh (no C/C++ files changed; Python files pass Ruff format check)
  • I have run pre-commit run --all-files and all hooks pass
  • Documentation is not required because no user-facing configuration or API was added
  • I have added tests to prove my changes are effective
  • RFC is not required because the change is under 500 lines

AI Assistance Disclosure

  • No AI tools were used
  • AI tools were used (specified below)

Codex assisted with implementation, test design, and validation. The submitter reviewed the resulting changes.

@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 introduces graceful shutdown capabilities to the Mooncake Store Service by integrating signal handling and an asyncio shutdown event during both startup and execution. It also adds comprehensive unit tests to verify the shutdown behavior. The review feedback recommends ensuring that the store is properly closed and cleared if a shutdown is requested immediately after a successful setup to prevent resource leaks, setting self.store = None after closing it in the stop method to ensure idempotency, and adding a comment to explain the second call to _unblock_shutdown_signals() in main.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +176 to +184
if shutdown_event is not None:
# setup() is synchronous. Give asyncio signal callbacks a
# chance to publish a shutdown requested while it ran.
await asyncio.sleep(0)
if shutdown_event.is_set():
logging.info(
"Store startup cancelled by shutdown request"
)
return False

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.

high

If shutdown_event is set immediately after self.store.setup() succeeds, the method returns False but leaves self.store initialized and active. This can cause resource leaks if the caller does not call stop(). To prevent this, we should close the store and set self.store = None before returning False.

Suggested change
if shutdown_event is not None:
# setup() is synchronous. Give asyncio signal callbacks a
# chance to publish a shutdown requested while it ran.
await asyncio.sleep(0)
if shutdown_event.is_set():
logging.info(
"Store startup cancelled by shutdown request"
)
return False
if shutdown_event is not None:
# setup() is synchronous. Give asyncio signal callbacks a
# chance to publish a shutdown requested while it ran.
await asyncio.sleep(0)
if shutdown_event.is_set():
logging.info(
"Store startup cancelled by shutdown request"
)
if hasattr(self.store, "close"):
self.store.close()
self.store = None
return False

Comment on lines +686 to +690
ret = self.store.close()
if ret != 0:
logging.warning("Mooncake service close returned %s", ret)
else:
logging.info("Mooncake service stopped")

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

To make the stop() method idempotent and prevent potential double-close issues or operations on a closed store, set self.store = None immediately after calling self.store.close().

Suggested change
ret = self.store.close()
if ret != 0:
logging.warning("Mooncake service close returned %s", ret)
else:
logging.info("Mooncake service stopped")
ret = self.store.close()
self.store = None
if ret != 0:
logging.warning("Mooncake service close returned %s", ret)
else:
logging.info("Mooncake service stopped")

Comment on lines +738 to +741
_unblock_shutdown_signals()
await asyncio.sleep(0)
if shutdown_event.is_set():
return

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

The call to _unblock_shutdown_signals() is repeated here after start_store_service. While this is likely intended to restore the signal mask in case the synchronous C++ store setup modified it, adding a brief comment explaining this non-obvious behavior would greatly improve maintainability.

Suggested change
_unblock_shutdown_signals()
await asyncio.sleep(0)
if shutdown_event.is_set():
return
# Unblock signals again in case the C++ store setup modified the signal mask.
_unblock_shutdown_signals()
await asyncio.sleep(0)
if shutdown_event.is_set():
return

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@zhangzuo21
zhangzuo21 marked this pull request as ready for review July 14, 2026 09:09
Aionw
Aionw previously requested changes Jul 14, 2026
Comment thread mooncake-wheel/mooncake/mooncake_store_service.py Outdated
@Aionw
Aionw dismissed their stale review July 16, 2026 03:34

Fixed

@ykwd
ykwd merged commit 64cc99f into kvcache-ai:main Jul 17, 2026
26 checks passed
fy2462 pushed a commit to fy2462/Mooncake that referenced this pull request Jul 20, 2026
* fix(wheel): handle store service shutdown signals

* fix(wheel): address shutdown review feedback
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants