mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #3523] [TENT] RDMA link-flap recovery gaps: hung WRs wedge QPs silently, swallowed bootstrap rejection, no PORT_ACTIVE fallback #902
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#902
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 @ai2open on GitHub (Aug 19, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/3523
[TENT] RDMA link-flap recovery gaps: hung WRs wedge QPs silently (no error WC, no software timeout, no reconnect), plus swallowed bootstrap rejection and missing PORT_ACTIVE fallback
Scenario
Prefill/Decode disaggregation (KV cache transfer) over RoCE with the TENT backend
(
MC_USE_TENT=1), RDMA NICs are bonded (mlx5_bond_*).Observed behavior: after PD transfer timeouts, or an RDMA NIC link down/up,
subsequent transfers never recover on their own. The classic (non-TENT) engine does
not show this behavior in the same environment.
Controlled experiment (single-host repro)
ip link set <netdev of mlx5_bond_4> down, wait ~4 min,ip link set up.Timeline (all from logs):
08:18:06— link down; all 8 engine processes correctly logAction: mlx5_bond_4 down.08:19:03-08:19:54—fallback device selectionfloods (~780k reroute attempts);throughput degrades but does not drop to zero (other NICs still serve).
08:19:35+— application-level 20 s timeouts (batch_transfer_sync_write TIMEOUT,ret=-1).
08:21:54— link up; all 8 processes correctly logAction: mlx5_bond_4 up(so PORT_ACTIVE handling worked in this run).
(25 each), but pp_rank 1/2 succeeded 25/25 while pp_rank 0/3 failed 25/25 —
and stayed broken until the end of the log (16+ min later).
The crucial part: for the failing ranks after link-up, the TENT layer logged
absolutely nothing:
Detected error WQE(no error completions)transfer timeout (software)(slice-level software timeout never fired)Unable to connect endpoint(no reconnect attempts/failures)Endpoint marked for destructionSlices were submitted at the app level and then vanished: never completed, never
errored, never reported by any TENT diagnostic path.
Root cause analysis
Defect A (primary, confirmed by experiment): hung WRs during link-down wedge the QP/endpoint forever
WRs posted to QPs on the downed NIC hang silently: while the port is down the
hardware neither transmits nor completes/errors them. After the link comes back,
those WRs still never complete and never produce an error CQE, so:
wr_depth/cq_outstanding) stays consumedforever;
behind dead WRs and are never sent;
TENT's detection hooks fire: no error WC (so no
disableEndpoint), and thesoftware slice timeout never triggers for them, so the endpoint is never
destroyed and rebuilt;
whose paths crossed the downed NIC never recovered (16+ min, until shutdown),
while ranks on other NICs recovered immediately.
There is currently no mechanism in TENT that reclaims an endpoint with
outstanding WRs after a link recovery (e.g. destroying endpoints with stale
outstanding WRs on
PORT_ACTIVE), and no visibility into stuck posted WRs(the classic engine gained exactly this diagnostic in #2872 via
MC_TRACK_RDMA_POSTED_SLICES; TENT has nothing equivalent).Defect B: bootstrap RPC swallows the peer's rejection reason → misleading "Missing peer GID in bootstrap"
ControlService::onBootstrapRdma(tent/src/runtime/control_plane.cpp) ignores thereturn value of
bootstrap_callback_. When the peer rejects the bootstrap (staleendpoint retired, device down, ...), the real reason goes into
local_desc.reply_msg, but the RPC still returns success with an emptylocal_gid,and the initiator reports the misleading
Missing peer GID in bootstrap(
tent/src/transport/rdma/endpoint.cpp). The classic engine fixed the equivalentissue in #2959 (structured handshake rejection replies); TENT still has it at
v0.3.12.
Defect C (latent, not triggered in this run):
PORT_ACTIVEcan be lost → context stuck in DEVICE_PAUSED foreverThe async fd is registered with
EPOLLET(tent/src/transport/rdma/context.cpp),but
Workers::monitorThreaddoesepoll_wait(..., maxevents=1, 100)andhandleContextEventsreads exactly oneibv_async_eventper wakeup(
tent/src/transport/rdma/workers.cpp). Under edge-triggered semantics, whenmultiple events queue (e.g. a fast flap),
PORT_ACTIVEmay never be processed.resume()has no other caller, and there is no periodicibv_query_portvalidation, so a context can stay
DEVICE_PAUSEDforever (makingWorkers::getEndpointfail all transfers on that local NIC permanently). Theclassic engine is saved from this by its monitor worker unconditionally
re-activating contexts every second; TENT has no such fallback. In our controlled
experiment (down ~4 min, single events far apart) this race did not trigger, but
it remains a latent risk for fast flaps.
Defect D: GID/LID cached at init and never refreshed
RdmaContextreadsgid_/lid_once inopenDevice()and always advertises thesecached values in bootstrap. If the GID changes after a flap while the process keeps
running, peers build AH entries from stale address vectors. The classic engine fixed
this in #2878 (refresh on HCA/GID change events); TENT still caches forever at
v0.3.12.
Related classic-engine fixes (none of them touch TENT)
MC_TRACK_RDMA_POSTED_SLICES)MC_RDMA_RAIL_PAUSE_SECONDS,structured handshake rejection replies
Verified at tag v0.3.12:
tent/src/runtime/control_plane.cppandtent/src/transport/rdma/workers.cppstill contain defects A–D.Suggested fixes for TENT
PORT_ACTIVE(context resume), proactively destroy endpoints that still haveoutstanding WRs instead of waiting forever for CQEs that will never arrive;
more generally, give an endpoint with stuck outstanding WRs a deadline after
which it is torn down and rebuilt.
"slice posted but never completed" is visible.
request queues waiting to be posted, not only posted inflight slices.
reply_msgor anerror status back to the initiator).
ibv_get_async_eventreturns empty on each epollwakeup, and add a periodic
ibv_query_port-based resume fallback.classic engine.
Happy to provide full logs or help test patches.
@github-actions[bot] commented on GitHub (Aug 19, 2026):
Thanks for opening this issue, @ai2open!
226425123A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide