mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #4418] Deadlock risk between snapshot restore and segment mount (segment_mutex_ / client_mutex_ ordering) #1114
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#1114
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 @quantz8a on GitHub (Sep 30, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/4418
Summary
MasterService::ApplySnapshotState()reverses the lock order used byMountSegment()/UnmountSegment(), which can deadlock when snapshot restore races with a client mount or unmount.Affected path
ApplySnapshotState()obtains an exclusive lock onSegmentManager::segment_mutex_viasegment_manager_.getSegmentAccess()(mooncake-store/src/master_service.cpp:11232).UnmountSegment()(:11238), andPing(client_id)(:11248).client_mutex_:Ping()doesstd::shared_lock<std::shared_mutex> lock(client_mutex_)(:8405).UnmountSegment()doesstd::shared_lock<std::shared_mutex> client_lock(client_mutex_)(:3127) before re-acquiringsegment_mutex_(:3133).Reverse order in the other callers
MountSegment()first takesclient_mutex_exclusively (:900) and then, inside its liveness callback, takessnapshot_mutex_andsegment_mutex_viasegment_manager_.getSegmentAccess()(:909-:913).UnmountSegment()takesclient_mutex_beforesegment_mutex_as shown above.So the code has both:
segment_mutex_ (unique) -> client_mutex_ (shared)inApplySnapshotState().client_mutex_ (unique) -> segment_mutex_ (unique)inMountSegment().Deadlock scenario
ApplySnapshotState(): it holdssegment_mutex_exclusively and blocks trying to takeclient_mutex_(viaPing).MountSegment(): it holdsclient_mutex_exclusively and blocks trying to takesegment_mutex_(viagetSegmentAccess()).A and B wait on each other, forming a classic ABBA deadlock. The same inconsistent ordering exists for the snapshot-restore unmount path.
Suggested fix
Do not call
UnmountSegment()orPing()while holding the snapshot-restoresegment_mutex_hold. Collect the unready/all segment lists undersegment_mutex_, release the lock, then unmount/ping/metric-update outside the lock. See the accompanying PR./cc @kvcache-ai/mooncake-core
@github-actions[bot] commented on GitHub (Sep 30, 2026):
Thanks for opening this issue, @quantz8a!
3152505A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide
@he-yufeng commented on GitHub (Oct 2, 2026):
Verified both deadlock faces on current main (
ApplySnapshotStateat master_service.cpp:11142):ScopedSegmentAccess(unique onsegment_mutex_) and callsUnmountSegmentfor each unready segment;UnmountSegmentitself entersScopedSegmentAccessat :3133.std::shared_mutexis non-recursive, so a second unique lock on the same thread is UB and blocks in practice. Any restore that actually has unready segments wedges the master right there.segment_mutex_, it callsPing, which takesclient_mutex_shared (:8405).MountSegmenttakesclient_mutex_exclusive first (:900) and then entersgetSegmentAccess()in its liveness callback. Restore racing a client mount has each side waiting on the other.So this is two distinct defects in one function, and the collect-then-act shape the issue suggests covers both.
One overlap worth settling before review effort gets duplicated: #4022 (Sep 11) already implements the same collect-under-lock, act-outside restructure for the unready path, with a test, and has been sitting without comments for three weeks. #4419 is the minimal version of the same idea. Same root cause, same fix shape; one of them should absorb the other. There is also prior art in #3152 (closed unmerged), which attacked the adjacent
snapshot_mutex_ordering in this same function, in case the ordering argument there is still useful.Context for prioritization: my open #3806 rewrites the repair side of this restore path. Whichever of #4022/#4419 lands, I will rebase and adapt #3806 to the new locking, so merging one of them does not strand the tolerance work.