mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #4565] [RFC]: [Store] NoF Storage Device Control Plane, Phase 2: Fault Isolation and Device-Level Graceful Drain #1164
Labels
No labels
ascend
auto-closed
bug
bug
CI/Build
Common
documentation
enhancement
feature request
good first issue
help wanted
high priority
MetaX
nightly-failure
pull-request
PyTorch Backend
question
RFC
Roadmap
stale
Store
Tests
Transfer Engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/Mooncake#1164
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @zxpdemonio on GitHub (Oct 10, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/4565
Changes proposed
Summary
RFC #4561 proposed a NoF storage device control plane for Mooncake Store, and Phase 1 landed in PR #4560: device metadata, a derived health state machine (UNKNOWN / HEALTHY / DEGRADED / FAILED / UNMOUNTING), a maintenance planner, and read-only admin observability.
Phase 1 made device health visible. It deliberately left the reaction of the master unchanged: after
nof_heartbeat_failures_thresholdconsecutive probe failures the heartbeat thread unmounts the segment, and nothing happens in between. There is also no operator-facing way to stop writing to a device, or to migrate its contents off it.This RFC covers Phase 2, which turns that health signal into action:
DrainJobmachinery to a device identity so thatmaintenance_planoutput can actually drive evacuation.isolate,unisolate,drain,drain_status.Reference implementation: #4566 (stacked on #4560). Related: #4431 (cross-client move/migration control plane), which owns the
DrainJoblifecycle this phase builds on.Motivation
The window between DEGRADED and unmount is unused
With threshold
Nand probe intervalI, Phase 1 reports DEGRADED afterN/2failures, while the alive timeout unmounts at roughlyN * I. That window currently exists only in the reported health - the master takes no action inside it. For devices backing offloaded objects this is the worst of both outcomes:No operator action short of unmount
Today there is no way to express "stop writing to this target, keep its data readable, and let me migrate it". Node maintenance, NVMe target firmware upgrades, and predicted disk failures all need exactly that, and all of them currently have to be done by pulling the client out from under the master.
Proposed design
1. Fence state machine
Phase 2 adds one reversible action between HEALTHY and the terminal unmount:
Two properties matter:
IsolateSegment()detaches the allocator registration of the segment and moves it toDRAINING. The segment stays mounted, itsbuf_allocatoris untouched, and no read path in the master gates onSegmentStatus::OK, so replicas already on the device remain readable and migratable.Note that fencing at DEGRADED rather than at FAILED is what gives the mechanism a window to act: FAILED is derived from the same threshold as the alive timeout, so a device that is only fenced at FAILED is unmounted in the same heartbeat iteration.
2. Fenced devices keep being probed
The heartbeat probe set is defined by one predicate, shared with on-demand probing:
This is load-bearing for two reasons:
Manual cordons are never lifted automatically; only entries in
auto_isolated_devices_are.3. Device-level drain
DrainStorageDevice(device_id, target_segments, max_concurrency):CreateDrainJob()over that segment. No second migration lifecycle is introduced.device_id -> job_idso device listings andmaintenance_plancan report drain progress.The whole operation is serialised by a dedicated mutex, so two concurrent admin requests cannot both pass the "already draining" check and orphan a job.
maintenance_planintegration:probe_degraded/probe_faileddevice_isolateddrainingunmounting4. Isolation and drain must not fight over segment status
Both fencing and draining are expressed as
SegmentStatus::DRAINING, and the existing drain cancel and drain failure paths restoreOKwhen they give up. Left alone, cancelling a device drain would silently put a cordoned device back into the allocation path while it was still reported as isolated.Rather than patching three call sites, the invariant is enforced where it belongs:
ScopedNoFSegmentAccess::SetSegmentStatusByName()refuses to setOKon a segment that has no allocator registration. Returning a fenced device to service is the job ofUnisolateSegment(), because that is the only path that re-registers the allocator. This mirrors what the DRAM-sideSetSegmentStatus()already does, and leaves non-isolated drains behaving exactly as before.5. Admin REST endpoints
/api/v1/devices/isolate?device_id=<id>/api/v1/devices/unisolate?device_id=<id>/api/v1/devices/drain{device_id, target_segments?, max_concurrency?}; returnsjob_id/api/v1/devices/drain_status?device_id=<id>GET /api/v1/devicesgainsisolated,draininganddraining_job_idalongside the Phase 1 fields.6. Prometheus metrics export
To support cluster-wide monitoring and alerting without polling
/api/v1/devices, the master exports device-level health, isolation, and drain metrics on the existing/metricsendpoint:mooncake_master_storage_devices_totalhealthhealthy,degraded,failed,unknown,unmounting)mooncake_master_storage_devices_isolated_totalmooncake_master_storage_devices_auto_isolated_totalmooncake_master_storage_devices_draining_totalmooncake_master_storage_device_recovery_candidatesmooncake_master_storage_device_gc_candidatesThese metrics refresh on scrape during the master storage telemetry pass, ensuring zero drift between REST query results and Prometheus timeseries.
7. Configuration
Automatic fencing is an operator-facing policy, so it has an off switch, plumbed through the config file, the gflag, and
MasterServiceConfig::builder(). Turning it off restores Phase 1 behaviour exactly: health is still reported, and the alive-timeout unmount still applies.Compatibility and impact
QueryDrainJob()becameconst, which is source-compatible for callers.USE_NOF=OFFstill builds clean.Testing
Unit and integration tests on a
USE_NOF=ONbuild:storage_device_metadata_test- pure-function coverage of health derivation, schedulability (including the isolation and drain axes), and maintenance-plan reasons.nof_heartbeat_test- live heartbeat coverage: DEGRADED fencing and auto-recovery, manual cordon keeps being probed and reported, cancelled device drain keeps the device fenced, the configuration off-switch, device drain job creation andmaintenance_planreporting, and the unchanged alive-timeout unmount.master_admin_server_test- REST contract tests for the four new endpoints plus the Phase 1 device endpoints.segment_test/segment_pool_test- existing segment lifecycle coverage, unchanged by the new status guard.Follow-ups (not in this phase)
maintenance_plan-driven controller that issues drains automatically instead of leaving the plan to an external operator.Before submitting a new issue...
@github-actions[bot] commented on GitHub (Oct 10, 2026):
Thanks for opening this issue, @zxpdemonio!
16206312A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide