mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #4660] [Bug]: TENT never releases per-peer state of departed peers (RDMA endpoints, 32 MiB pinned each, and idle RPC client connections) #1183
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#1183
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 @xiaodouzi666 on GitHub (Oct 14, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/4660
Bug Report
What happens
A TENT engine keeps per-peer state keyed by the peer's segment name (
ip:portin P2P mode). When a peer goes away and is never contacted again — it exits, or restarts and comes back under a newip:port(rpc_server_port=0picks a random port) — that state is not released. Line numbers below are as of342c8aee.SIEVEEndpointStore(src/transport/rdma/context.cpp:320), and anRdmaEndPointis created lazily for every (local NIC, peer NIC) pair that carries traffic or that the peer bootstraps. Every endpoint'sconstruct()allocates and registers two notification buffers ofkNotifyBufferSize(64 KiB) ×kNotifyMaxPendingSends(256) = 16 MiB each (include/tent/transport/rdma/endpoint.h:303-304,src/transport/rdma/endpoint.cpp:187-210): 32 MiB of pinned memory per endpoint, plus1 + num_lanesQPs, whether or not notifications are ever used.closeSegment()only drops the segment handle mapping (src/runtime/transfer_engine_impl.cpp:735-738→SegmentManager::closeRemote,src/runtime/segment_manager.cpp:62); no transport is told. An endpoint is retired only on a QP error or fatal event on it (workers.cpp:671-677,rdma_transport.cpp:1130-1131), a local port event (context.cpp:520), a new bootstrap from the same NIC path (rdma_transport.cpp:860-866; the comment there reads "Endpoints are never reset"), engine shutdown, or capacity eviction, which runs only when a new key is inserted after a successfulconstruct()(endpoint_store.cpp:263) with a cap ofendpoint_store_cap= 65536 per local NIC (params.h:55). An idle endpoint of a peer that left under a name that never comes back hits none of these.CoroRpcAgentkeeps oneClientPoolper server address (include/tent/rpc/rpc.h:126). A client goes back to the pool after every successful call (src/rpc/rpc.cpp:104), so the pool keeps as many idle connections as the peak concurrency towards that peer; it is cleared only when a later call to the same address fails or onstop()(rpc.cpp:167-168). A departed peer is never called again, so its connections stay open inCLOSE-WAIT.Measured (two hosts, 4 RoCE NICs each; one long-lived engine serving 400 short sessions, each from a freshly started peer engine on a new
ip:port):CLOSE-WAIToutbound connections to the departed peers' RPC ports (7 per peer), and ~15 more each of eventpoll/timerfd/eventfd; the uverbs descriptors stay constant;RLIMIT_MEMLOCK, once the limit is reached new peers are refused (Cannot allocate endpoint; the notification-bufferibv_reg_mrfails withENOMEM), and capacity eviction does not help because it only runs after a successfulconstruct();Why it matters
Any long-running TENT process whose peers churn — prefill/decode workers restarted by an orchestrator, P2P segments with
rpc_server_port=0— accumulates pinned memory and descriptors untilRLIMIT_MEMLOCK, memory orRLIMIT_NOFILEruns out. LoweringMC_MAX_EP_PER_CTX/endpoint_store_capbounds the endpoint part today, at the cost of also evicting live endpoints; nothing bounds the RPC pools.Possible directions
Give the transports and the RPC agent a hook when a segment is closed or when the control plane learns that a peer is gone, then retire that peer's endpoints with the existing
RdmaEndPoint::resetConnection()(endpoint.cpp:700, already used byWorkers::disableEndpointand by the notify path'sRetireEndpoint) and drop itsClientPool; or an idle-time eviction in the endpoint store and the RPC pools, independent of the capacity cap; or allocate the notification buffers lazily, on the first notification. Happy to help with a patch if one of these is the preferred shape.Reproduction
Engine A stays up; in a loop, start engine B (P2P,
rpc_server_port=0),openSegment(A), WRITE one buffer to A and send A one notification, exit. WatchVmPinin/proc/<A>/statusandss -tnpfor A. We measured it with the diagnostics tool we are preparing for #2864 (on top of #4154); its reports can be attached.Environment: commit
342c8aee(first seen on an earlier main,e4229791f3); Linux x86_64 hosts with 400 Gb/s mlx5 RoCE v2 NICs; no GPU involved.Before submitting...
@github-actions[bot] commented on GitHub (Oct 14, 2026):
Thanks for opening this issue, @xiaodouzi666!
77219630A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide