[CCF Archive] Store object type eviction policy submission #3

Closed
kancel wants to merge 382 commits from kancel:ccf-archive-pr2746 into main
First-time contributor

本 PR 只作为比赛作品提交要求的 GitLink 归档。

由于 GitLink 的主分支落后于 GitHub 侧,本 PR 不作为上游代码评审的主要入口。具体提交与技术讨论请参考:

本 PR 的目标是满足比赛要求中的 GitLink 托管/归档要求。

本 PR 只作为比赛作品提交要求的 GitLink 归档。 由于 GitLink 的主分支落后于 GitHub 侧,本 PR 不作为上游代码评审的主要入口。具体提交与技术讨论请参考: - https://github.com/kvcache-ai/Mooncake/pull/2689 - https://github.com/kvcache-ai/Mooncake/pull/2743 `[Store] Add hidden state object data type` - https://github.com/kvcache-ai/Mooncake/pull/2744 `[Store] adjusted lease timeout policy` - https://github.com/kvcache-ai/Mooncake/pull/2745 `[Store] Add object type accounting to BatchEvict` - https://github.com/kvcache-ai/Mooncake/pull/2746 `[Store] object type eviction policy` 本 PR 的目标是满足比赛要求中的 GitLink 托管/归档要求。
kancel added 382 commits 2026-07-06 19:33:41 +08:00
634b709731
[TE] EFA SRD shared-endpoint refactor: drop per-peer fid_ep model (#1944)
* feat(efa): idempotent warmupSegment for repeated calls

Adds EfaContext::peekEndpoint() — a non-creating, normalized-key lookup
— and uses it in EfaTransport::warmupSegment() to short-circuit when
every (local_ctx, peer_nic) pair is already connected. Repeated warmup
calls on the same segment now return immediately instead of firing a
256-thread std::async fan-out each time.

Verified on p6-B300 (16 NICs × 16 peer NICs = 256 endpoints):
  iter 0: 7.95s  (full handshake)
  iter 1: 0.000s (short-circuit hit)
  iter 2: 0.000s (short-circuit hit)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(efa): evict-on-ENOMEM + drop-on-failure for endpoint store

Recover from QP exhaustion (fi_enable returns -FI_ENOMEM at the device
cap, 768/device on p6-B300) and prevent dead endpoints from piling up
under callers that drift endpoint keys (e.g. keys carrying a timestamp).

Three places:

* EfaContext::endpoint (slow path): on construct failure, evict stale
  endpoints once and retry. Eviction only scans when we actually ran
  out of room, so normal traffic pays nothing.

* EfaTransport::warmupSegment: if setupConnectionsByActive fails, drop
  the endpoint via deleteEndpoint(normalizedKey) instead of leaving a
  dead fid_ep holding its QP slot.

* EfaContext::submitPostSend: if submitPostSend returns non-zero and
  the endpoint is no longer connected, drop it for the same reason.

Reproduced on B300: without the fix, injecting ":drift<iter>" into the
peer_nic_path exhausts fi_enable at iter 48 (768/16 peer NICs). With
the fix, 325 iterations complete with RSS bounded at ~6 GB.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(efa): SRD shared-endpoint refactor — drop per-peer fid_ep model

Under SRD (FI_EP_RDM), a single fid_ep per local NIC can address any number
of peers via fi_av_insert.  The old model created 16 per-peer endpoints per
connection (one per local NIC), consuming 16 QPs each — hitting the 768 QP
cap after only 48 peers and forcing an LRU + evict-on-ENOMEM dance.

This change:
- EfaContext owns a single shared fid_ep + peer address vector (`peer_map_`)
- EfaEndPoint becomes a thin per-peer fi_addr_t holder (no fi_endpoint,
  no fi_enable) — handshake is RPC + fi_av_insert only
- submitPostSend delegates to EfaContext::submitSlicesOnPeer, using the
  peer's AV index against the shared endpoint
- setPeerNicPath detaches stale AV slot on peer reconnect so drift scenarios
  re-handshake cleanly
- Deletes EfaEndpointStore + LRU/ENOMEM eviction machinery — no longer needed
- warmupSegment short-circuit updated for the shared-endpoint flow

P5EN drift stress (1 target ↔ 1 initiator, customer shape 328 GB, 16 NIC,
180s target lifetime):
  - iter 0 (fresh peer) first_batch: 33.6 ms   (was ~8.95 s → 266× faster)
  - iter 1–3 (drift)   first_batch: 260–340 ms (was ~8.95 s →  ~26× faster)
  - steady: ~215–220 GB/s, 0 failures across 204 M completed ops
  - QP growth per added peer: 0 (was 16) — no more 48-peer cliff

See mooncake-transfer-engine/example/efa_srd_refactor_validation/ for raw
CSV and target-teardown timings.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(efa): expose warmup_efa_segment in Python binding

The C API (warmupEfaSegment) and Rust FFI (warmup_efa_segment) already
pre-connect every (local_ctx, peer_nic) pair so the first submitTransfer
does not stall on handshake RPC + fi_av_insert.  The pybind TransferEngine
did not expose this, so Python-driven integrations (vLLM / SGLang) could
not benefit from the first-call latency improvement from the SRD shared-
endpoint refactor (iter 0 first_batch 33.6 ms vs 8.95 s baseline).

Mirror the C API shape: thin pass-through that dynamic_casts the installed
"efa" transport and calls EfaTransport::warmupSegment.  No-op on non-EFA
builds or when the EFA transport is not installed.

Tested: built with USE_EFA=ON on p5en, imported engine.TransferEngine, and
confirmed `warmup_efa_segment(segment_name: str) -> int` is bound.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(efa): apply code_format.sh (clang-format-20)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(efa): drop internal drift_stress/validation dirs + stale gflags doc note

- Remove mooncake-transfer-engine/example/drift_stress/ — depends on
  the out-of-tree customer_pattern Rust binary and isn't usable by
  upstream contributors.
- Remove mooncake-transfer-engine/example/efa_srd_refactor_validation/
  — validation artifacts for the SRD refactor; the headline numbers
  live in the PR description instead.
- Remove the libgflags-dev note from docs/.../efa_transport.md — it
  is pulled in transitively by dependencies.sh and no longer needs a
  manual install step.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(efa): close shared endpoint before dropping peer map in teardown

Calling fi_av_remove() on peer addresses after the shared fid_ep was
still open — or calling it at all during context teardown when the AV
itself is about to be fi_av_close()d — trips a libfabric EFA-provider
assertion, causing the efa_transport_test unit tests to segfault on
engine destruction.

Reorder EfaContext::deconstruct() to close shared_ep_ first, then
detach every peer handle via a new markDetachedForTeardown() that just
resets the AV-slot field without calling fi_av_remove(). fi_av_close()
below invalidates every slot in one shot, so no per-slot removal is
required on the teardown path.

Verified: all 5 EFATransportTest cases (InstallTransport, LoopbackWrite,
WriteAndRead, MultiWrite, StressMultipleBatches) now pass on P5EN.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(efa): add coverage for warmupSegment, batch register, large xfer, re-open

Five additional EFATransportTest cases, filling gaps in the existing
suite (which only exercised install + 4 single-buffer write/read paths):

  - WarmupSegmentLoopback  — covers EfaTransport::warmupSegment() and
    its idempotent short-circuit on the second call, plus the empty /
    self-name no-op path.
  - WarmupSegmentNotFound  — warmupSegment must fail cleanly (not hang)
    for an unknown segment name.
  - RegisterMemoryBatch    — registerLocalMemoryBatch +
    unregisterLocalMemoryBatch round-trip.
  - LargeTransfer          — 128 MB buffer, 64 x 1 MB slices; exercises
    WR / CQ pacing in EfaContext::submitSlicesOnPeer well past the
    16 x 64 KB MultiWrite ceiling.
  - RepeatedOpenSegment    — openSegment() on the same peer repeatedly
    must keep transferring correctly; guards the setPeerNicPath detach
    path that target-restart drift depends on.

docs/.../efa_transport.md's Unit Tests table updated to match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): restrict CTest invocation to the efa tests

Running the full ctest suite on an EFA-only host trips etcd /
mooncake_master requirements in the TCP / metadata / store tests, which
can read as "EFA broke the build". The EFA doc should not recommend
that invocation — filter to the two EFA binaries and call out the
reason in a note.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): drop redundant MC_METADATA_SERVER / MC_LOCAL_SERVER_NAME block

The defaults (P2PHANDSHAKE and 127.0.0.1:12345) are what the loopback
unit tests actually want, and nothing in the current test suite needs
an override, so documenting these "env var exports" next to the test
invocation just makes readers wonder whether they are mandatory.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): drop obsolete CUDA_VISIBLE_DEVICES tip for CPU-to-CPU runs

The tip existed because libfabric's EFA provider used to dlopen
libcudart at fi_getinfo time, creating a CUDA context even for
CPU-only benchmarks. EfaContext::construct now sets FI_HMEM=system
when the build is not GPU-enabled, which already prevents that
context from being created — the CUDA_VISIBLE_DEVICES="" workaround
is no longer needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): lead with a bench command that hits line rate

The previous "Initiator Node" snippet used block_size=65536 /
threads=8 / buf=1GB, which reaches only ~50 GB/s on an 8x400G host
and reads as "EFA is broken" to anyone copy-pasting it. Replace with
block=1MB / threads=32 / buf=4GB — the same values that produce the
GB/s numbers quoted in the Benchmark Results tables — and add a short
sentence telling readers exactly why block_size matters. Also clarify
that the MC_SLICE_SIZE no-effect note covers both CPU-to-CPU and
GPU-to-GPU paths (verified: no EFA code reads globalConfig().slice_size,
only rdma_transport and kunpeng_transport do).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): drop MC_SLICE_SIZE references, sync target --buffer_size

EFA transport never reads globalConfig().slice_size — the only readers
are rdma_transport and kunpeng_transport — so the env-var table row,
the "MC_SLICE_SIZE has no effect" tuning-tip bullet, and the
pre-optimization tuning-history details block were all documentation
debt rather than user guidance. Drop all three.

Also: the target command defaulted to --buffer_size=1GB (the flag's
default), but the recommended initiator command now uses 4GB. Since
the target allocates and registers exactly FLAGS_buffer_size bytes and
the initiator writes into offsets [0, buffer_size), they have to
match. Add --buffer_size=4294967296 to the target snippet and a short
line calling out the constraint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(efa): skip preTouchMemory for GPU buffers, clarify target flags

preTouchMemory() does a CPU-side store per page, which was gated only
by "chunk >= 4 GiB". That trips a segfault the moment a user registers
a >=4 GiB cudaMalloc buffer — exactly what the recommended benchmark
config does. Restrict the pre-touch to host (cpu:*) memory so VRAM
registration stays a pure libfabric call.

Also amend the benchmark docs:
  - call out that --use_vram / --gpu_id apply to the target too, and
    that GPU-to-GPU runs need --gpu_id=-1 on both sides
  - mention --use_vram=false as the alternative to a -DUSE_CUDA=OFF
    build for CPU-to-CPU runs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): default benchmark snippet to gpu_id=-1 + threads=16

Align the benchmark quick-start with what actually reaches line rate
on a p5en.48xlarge pair (≈350 GB/s write, verified). The previous
snippet recommended --threads=32 and no --gpu_id override, which:

  - ran on one GPU (default gpu_id=0), so buffers sat on one NUMA
    node and half the NICs were cross-NUMA — settled at ≈56 GB/s
  - ran into the SRD shared-endpoint WR cap (16 × 256 = 4096 WRs
    vs 32 × 128 = 4096 outstanding slices, zero headroom) and
    triggered "timed out waiting for CQ drain"

Rework the section: lead with the two knobs that matter (--gpu_id=-1
on both sides, --block_size=1048576), add --gpu_id=-1 to the target
snippet for symmetry, switch the initiator to --threads=16, and
explain in a note why 16 is the ceiling (so readers know where the
limit comes from, not just that we picked it).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): complete the Key Parameters table

The table was missing several flags that the bench snippet in the
same section actually uses (--mode, --protocol, --metadata_server,
--segment_id, --use_vram), and some of the flags it did list had
defaults / descriptions that did not match the binary (e.g. default
operation is read, not write). Sync every row to DEFINE_* in
transfer_engine_bench.cpp and add explicit "what this is for on EFA"
hints next to the two flags (--block_size, --gpu_id) that most
determine whether the run hits line rate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): reorder benchmark sections by peak throughput

Lead with p6-b300 (752 GB/s peak), then p5en (347 GB/s), then
p6-b200 (313 GB/s). Readers skim the first table to decide whether
EFA transport is fast enough for them; putting the best-performing
platform first avoids undersell.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): reorder benchmarks by hardware generation, not raw GB/s

Previous reorder put p5en (H200) above p6-b200 (B200) because the
p5en rows happen to show a higher headline number, but B200 is a
newer generation than H200 and the p6-b200 numbers there are an
older snapshot. Fix the ordering to reflect hardware recency:
p6-b300 → p6-b200 → p5en. Same change applied to the
Cross-Transport Comparison table.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): refresh p5en bench results (366 GB/s write / 304 GB/s read)

Re-swept on P5EN (H200, 16×200G) with the SRD shared-endpoint build
and --buffer_size=4GB per GPU. New peaks:

  - Write: 365.66 GB/s @ threads=16, batch=128 (up from 347 GB/s)
  - Read:  303.78 GB/s @ threads=16, batch=32  (up from 308 GB/s,
           but now with a clear optimal config)

The new table preserves enough rows to show the two interesting
axes — write wants big batches, read wants small ones — and the
Cross-Transport Comparison is updated to 366 GB/s (~91% of the
400 GB/s line rate). B200/B300 rows are unchanged (no hardware
available to re-sweep).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): mark B200/B300 bench rows stale, refresh p5en CPU, drop cross-transport table

- p5en CPU-to-CPU re-swept on current SRD shared-endpoint build:
  peak write 213.57 GB/s @ threads=48/batch=32, peak read 212.18 GB/s
  @ threads=16/batch=32 (vs old 192 GB/s / 182 GB/s). Table shows a
  few representative points — throughput is essentially flat across
  the parameter grid because the run is DRAM-bound.
- B200 and B300 sections tagged as predating the SRD refactor and the
  recent tuning work; a re-sweep is pending hardware availability.
- Drop the "Cross-Transport Comparison" subsection and the
  "EFA vs RoCE RDMA" paragraph — both were derived numbers that
  quickly rot and were already redundant with the per-platform
  tables above them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): add block_size + buffer_size sweep tables as evidence

Two sweeps on p5en GPU-to-GPU (SRD shared-endpoint build, --gpu_id=-1)
at the peak config (threads=16, batch=128), backing up the tuning
claims elsewhere in this file:

  - block_size: 64 KB default reaches only 26% of peak. Write
    throughput climbs steeply through 512 KB, plateaus between 1 MB
    and 2 MB. 2 MB is MC_EFA_STRIPING_THRESHOLD (a different code
    path). 1 MB is the recommended value — within 4% of the 2 MB
    peak and safely below the striping boundary.
  - buffer_size: only needs to clear block × batch × threads (2 GB
    in the peak config). 2 GB vs 4 GB differs by ~3% on write, read
    is flat within noise. The example commands use 4 GB purely as
    a generous default, not because smaller fails.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): rewrite Tuning Tips around the SRD shared-endpoint model

Two stale claims under Tuning Tips were written for the old per-peer
endpoint code:

  - "Increase threads to 32-48": under the shared endpoint each NIC
    has a 256 WR cap, and threads × batch past 16×256 = 4096 trips
    the CQ-drain timeout. The sweep on p5en shows 16 threads is the
    ceiling, not the floor.
  - "Write peaks at threads=32, read at threads=16 / buffer=2GB max
    per GPU": both taken from the pre-refactor B300 snapshot.
    Current p5en peaks are both at threads=16; the 2GB/GPU limit
    was the VRAM preTouch segfault that is now fixed.

Rewrite the bullets to describe what the SRD shared-endpoint
actually constrains: block_size sweet spot, threads × batch WR
cap, the write-vs-read batch-size split, and --gpu_id=-1 on both
sides. Add an explicit line that buffer_size only needs to clear
block × batch × threads (pointing readers at the sweep table).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(efa): remove broken per-request striping across NICs

submitTransferTask had a branch that, when request.length exceeded
MC_EFA_STRIPING_THRESHOLD (default 2 MB), split the request into
num_nics slices and dispatched one per NIC. The intent was to speed
up large single transfers by paralleling across all NICs.

A sweep on p5en (SRD shared-endpoint build, 16 NICs x 200G) shows
the branch is a ~20x negative optimization in every bench config:

  block=1 MB, threads=16, batch=128: ON 16 GB/s vs OFF 366 GB/s
  block=8 MB, threads=16, batch=16:  ON 18 GB/s vs OFF 355 GB/s
  block=32 MB, threads=16, batch=4:  ON 18 GB/s vs OFF 328 GB/s

Even the designed-for scenario (threads=1, batch=1, single huge
request) only wins by 1.2x (27 vs 23 GB/s) — far below the 16x
expected from parallel dispatch, because the per-slice post_lock /
peer lookup still serialize the dispatch. And that "win" is only
reachable with threads=1 — any realistic caller with multiple
threads can parallelize on its own without this mechanism.

Since the branch silently degrades every common bench / workload
configuration above the 2 MB threshold and delivers no real win
even in its best case, delete it:

  - efa_transport.cpp: drop the LARGE TRANSFER if-branch and the
    kStripingThreshold read.
  - config.h / config.cpp: drop the efa_striping_threshold field
    and the MC_EFA_STRIPING_THRESHOLD env var.
  - docs/.../efa_transport.md: drop the Environment Variable row,
    the "Note on EFA slicing" striping explanation, and the
    "striping off" callouts in the benchmark tables and tuning
    tips.

Verified: all 10 EFATransportTest cases pass, and the peak p5en
bench run is unchanged (352.81 GB/s write @ threads=16, batch=128).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): number the sequential subsections for easier skimming

Four groups of level-3 / level-4 headings in this doc read as
step-by-step instructions (do A first, then B), but only two of
them were numbered while the others relied on order alone. Result:
readers had to count positions to tell "how far through" they were.

Number every such group consistently:

  - Performance Benchmark: Target Node (1) / Initiator Node (2)
  - Usage with vLLM: Prefill Instance (1) / Decode Instance (2)
  - Benchmark Results: p6-b300 (1) / p6-b200 (2) / p5en (3)

Reference-style sections (Key Parameters, Tuning Tips, Warmup,
Technical Details, Troubleshooting) stay unnumbered — those aren't
ordered and numbering them would imply a reading path that
doesn't exist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* test(efa): add efa_first_submit_probe for warmup / first-submit timing

A standalone cross-node probe that measures, separately:
  - warmupSegment() latency, and
  - per-submit latency for the first N single-transfer submits

so we can isolate the handshake / av_insert cost (warmup=OFF makes
it land on submit #0) and compare pre-refactor vs post-refactor
code without having to squint at 10s throughput averages.

Not enabled by default ctest — only built when USE_EFA=ON. Meant
for manual two-host runs alongside transfer_engine_bench.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): replace warmup numbers with a cross-branch p5en measurement

The old "Eager endpoint warmup" numbers (4 s stall / 13.5 ms warmed)
were from a B300 snapshot that predated the SRD shared-endpoint
refactor, so they described the OLD code. Replace with a fresh
cross-node measurement on p5en that compares both branches head to
head using efa_first_submit_probe:

  - Warmup itself: 1.1 s (SRD shared endpoint) vs 17 s (per-peer
    fid_ep on upstream main) — ~15x faster and much more stable
    (SRD runs were all 1.13-1.14 s; old code swung 9-17 s across
    three reps).
  - Cold first submit with NO warmup: 26 ms (SRD) vs 99 ms (old) —
    ~4x faster, because the shared endpoint removes the per-peer
    fi_endpoint / fi_enable step that used to dominate the first
    send.

The prior text's "first-batch stall" framing also reads as if the
whole first submitTransfer always pays the full handshake cost.
That's not quite how it works — the initiator round-robins across
local NICs, so only a handful of pairs warm on each call, and the
stall amortizes across the first several submits. Fix the wording.

Add a pointer to the probe source (example/efa_first_submit_probe.cpp)
so the measurement is reproducible.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): reframe "first-request latency" around two audiences

The previous version led with a long explanation of warmupSegment and
its internals, which made it sound like callers had to opt in to get
any first-request speedup from this PR. That isn't the case: the SRD
shared-endpoint refactor cuts the cold submit from 99 ms to 26 ms
automatically, and vLLM / SGLang (which don't call warmupSegment) see
that 4x win without any integration work on their side. The explicit
warmupSegment API is an additional 15x improvement on top of that,
but it matters mainly to direct Mooncake callers.

Restructure the section accordingly:

  - Lead with a 2x2 table showing both numbers side by side.
  - Frame the ~4x cold-submit win as the default, no-code-change
    benefit everyone gets.
  - Frame warmupSegment as the opt-in path for callers that want
    sub-10 ms first-request latency.
  - Explicitly note that vLLM / SGLang currently don't call it.
  - Keep the peer handshake-daemon bottleneck note so readers
    understand why the warmup scales linearly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): reference #1944 explicitly instead of "this PR"

"This PR" dangles once the doc lands on main — it's fine in review
context, less fine afterwards. Use the concrete issue number so
readers (including future us) can follow the link.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): unwrap soft-wrapped paragraphs, let the renderer flow them

Several blockquote notes, tuning-tip bullets, and the "first-request
latency" section used ~70-column soft wraps, which look fine rendered
but make the raw markdown look ragged — the line after 'Peer
addressing resolves lazily:' used to stop before the line was
actually full, which reads as a formatting bug in plain text. Let
each logical paragraph / bullet be a single line and defer wrapping
to the browser / sphinx renderer. No rendered change.

Also update the PR body on #1944 the same way.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): fix post-refactor stale references (architecture, thread safety, comparison)

Four places still described the pre-#1944 code:

  1. docs/.../efa_transport.md "EFA Transport Architecture" diagram —
     showed fid_ep inside EfaEndpoint, which is wrong under the
     shared-endpoint model. fid_ep lives on EfaContext (shared_ep_);
     EfaEndPoint only holds the peer's fi_addr_t AV slot. Redraw.

  2. docs/.../efa_transport.md "Thread Safety" — said the transport
     "adds per-endpoint spinlocks" (plural, per-endpoint). In the
     new code there's exactly one post_lock_ per EfaContext guarding
     the shared fid_ep. Reword.

  3. docs/.../efa_transport.md "EFA vs RoCE RDMA" comparison rows —
     used the pre-refresh p5en GPU number (347 GB/s) and the stale
     B200 row (313 GB/s). Replace with the current p5en peak from
     the benchmark tables above (365 GB/s GPU, 213 GB/s CPU), drop
     the row pointing at a now-absent B200 re-sweep.

  4. efa_context.cpp:589 — "Fast path: peer info pre-resolved by
     submitTransferTask's striping path". The striping path was
     removed in an earlier commit in this PR; the fast path now
     just catches slices whose peer_nic_path the caller already
     filled in. Reword without referencing the removed mechanism.

No behavioral change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(efa): address pr review — drop unused params, sysconf once, drop friend-class, expand probe comment

Addresses trivial review feedback on #1944:

  - EfaContext::construct() no longer takes num_comp_channels / port /
    gid_index. These three parameters were plumbed through from the
    original RDMA-era signature but the EFA/libfabric path never read
    them; the previous commit just silenced warnings with (void) casts.
    Remove them from the signature and the one caller in
    EfaTransport::installSglangTransport.
  - preTouchMemory() was calling sysconf(_SC_PAGESIZE) twice (once
    for the >0 check and once for the value). Cache the result in a
    local.
  - `friend class EfaEndPoint` in efa_context.h was unnecessary —
    EfaEndPoint only touches public methods on EfaContext
    (nicPath, localEpAddr, engine, insertPeerAddr, removePeerAddr,
    submitSlicesOnPeer). Drop the friend decl.
  - Expand the header comment on efa_first_submit_probe.cpp to say
    what problem it exists to measure and why transfer_engine_bench
    alone isn't sufficient — reviewer asked what the example is for.

No behavioral change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* perf(efa): faster hex<->binary AV address handling

Reviewer pointed out two sources of overhead in the handshake path:

  1. localEpAddr() built the hex string via std::ostringstream with
     std::hex / std::setw / std::setfill, which pulls in stream
     formatting for every byte. Replace with a direct table-driven
     encoder writing into a pre-sized std::string.
  2. insertPeerAddr() decoded the hex using substr() + strtol() for
     every byte (a temporary std::string and a libc base-16 parse per
     pair). Replace with a single pass using a hexNibble() helper and
     a pre-sized buffer.
  3. Loopback called insertPeerAddr(localEpAddr()), encoding the
     bytes to hex and then immediately decoding back. Expose the raw
     bytes via localEpAddrBytes() and add an insertPeerAddrBytes()
     overload; loopback now skips the round-trip entirely.

Handshake path is not the dominant cost in steady-state serving, but
warmupSegment() fires 256 of these on a fresh 16x16 topology and
every one of them was doing the extra work. The new path also avoids
the per-byte std::string allocation (strtol path).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* refactor(efa): use std::atomic for wr_depth / cq outstanding, fix O(N^2) erase

Replace `volatile int` + __sync_* builtins with std::atomic<int> on the
shared-endpoint pacing counters (EfaContext::wr_depth_ and
EfaCq::outstanding).  `volatile` never implied atomicity under the C++
memory model, and mixing it with __sync builtins was technically UB; the
atomic version is both portable and more explicit about the intended
memory ordering (acq_rel on the mutating ops, relaxed on the speculative
load used for CAS).

Also fix an O(N^2) batch consumption pattern in submitSlicesOnPeer:
`slice_list.erase(begin, begin + batch_count)` shifted the tail on every
iteration.  Consume via a moving cursor instead, and splice retry slices
in place when -FI_EAGAIN forces a partial post.

Behavior is unchanged on the happy path; the retry path now preserves
the original slice order at the cursor, matching the previous insert-at-
front semantics.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* docs(efa): drop stale efa_latency_bench.{py,png}, document efa_first_submit_probe

The `efa_latency_bench.py` script and its `efa_latency_bench.png` output
shipped with the original EFA transport PR (#1509) two years ago. No
markdown doc references them, the script hard-codes old private IPs as
defaults, and nothing guarantees the chart is still representative.
Remove both.

Expand the First-request latency section of efa_transport.md to actually
document `efa_first_submit_probe`: what it measures (cold-submit +
eager-warmup cost, which `transfer_engine_bench`'s 10s average hides),
how to run it (target / initiator commands), the expected output layout,
all flags, and the two situations where it is worth running (deciding
whether your app needs `warmupSegment()`, and comparing PR branches on
the same hardware). Marked it explicitly as EFA-specific — RDMA/TCP
transports do not have an equivalent warmup concept, so folding this into
`transfer_engine_bench` would muddy the generic benchmark for no gain.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: EC2 Default User <ec2-user@ip-172-31-8-212.us-west-1.compute.internal>
fe33367062
[docs] Document TCP port exhaustion and pool limitations (#1954)
* [TE][docs] Add TCP port-exhaustion troubleshooting entry

* [TE][docs] Document TCP connection pool known limitations
489c020778
[transfer_engine] fix: drain endpoint waiting list via periodic reclaim (#1952)
* [transfer_engine] fix: add periodic endpoint reclaim from monitorWorker

reclaimEndpoint() is currently invoked only from RdmaContext::endpoint()
after a new insertion. Under healthy load, insertions and evictions are
1:1 so this works. Under failure load -- many error completions trigger
deleteEndpoint(), but new-insertion traffic stalls because the dead peer
isn't generating new connection paths -- waiting_list_ grows without
bound and QPs never get destroyed.

Add a 1Hz reclaimEndpoints() call from monitorWorker on the existing
1-second context heartbeat. This decouples reclaim cadence from
insertion traffic.

See issue #1845.

* [transfer_engine] test: endpoint_store reclaim coverage for #1845

Adds unit + integration coverage for the periodic reclaim fix.

endpoint_store_test (5 tests, no RDMA device, runs under ctest):
  - reclaim drains quiescent entries on its own
  - reclaim leaves active entries alone (gate preserved)
  - reclaim is idempotent when empty
  - leak manifests without reclaim call (1118-eviction mirror of reporter)
  - reclaim works without active map (guard against insert/reclaim coupling)

endpoint_store_integration_test (requires RDMA device, not auto-registered):
  - Verifies WorkerPool::monitorWorker actually calls reclaimEndpoints at
    ~1 Hz by constructing a real RdmaContext and waiting for the tick to
    drain injected entries. Confirms the end-to-end fix wiring.

Supporting changes:
  - EndpointStore::waitingListSize() accessor (diagnostics + tests)
  - SIEVEEndpointStore::testOnlyInsertWaiting() for test injection
  - RdmaContext::endpointStore() accessor (diagnostics + tests)

* [docs] note periodic reclaim behavior and #1845 symptom

- design/transfer-engine: add a sentence to Endpoint Management explaining
  that waiting_list_ drains both on insertion and on the monitorWorker
  heartbeat, so accumulated reclaim does not stall under failure load.
- troubleshooting: extend the "Failed to create QP: Cannot allocate
  memory" entry with a bullet pointing at issue #1845 so operators
  seeing the symptom find the cause and the fix.

* [transfer_engine] fix: guard FIFOEndpointStore::waitingListSize with atomic counter

Per PR #1952 review: FIFO variant returned waiting_list_.size() on
std::unordered_set without holding endpoint_map_lock_, racing
concurrent modification. Mirror the SIEVE pattern with an atomic
waiting_list_len_ incremented in delete/evict, decremented in reclaim.

* [transfer_engine] test: suppress intentional RdmaTransport leak under LSAN

CI build (3.10/3.12) runs with -DENABLE_ASAN=ON and LSAN flagged the
5 × 288 byte allocation the test fixture intentionally leaks
(~RdmaTransport dereferences a null metadata_ unless install() ran).
Gate on __SANITIZE_ADDRESS__ / __has_feature and mark the pointer with
__lsan_ignore_object so real leaks are still caught.

* [transfer_engine] fix: widen waiting_list_len_ atomic to size_t

waitingListSize() returns size_t but the underlying counter was atomic<int>,
which quietly narrowed on load. Promote to atomic<size_t> in both FIFO and
SIEVE so the getter is a clean pass-through with no implicit conversion.

* [transfer_engine] docs: pin reclaimEndpoint lock contract on base interface

monitorWorker now calls reclaimEndpoint() via RdmaContext; it already
acquired endpoint_map_lock_ internally, but nothing declared that. Codify
the precondition on the base so future callers know not to hold the lock.
RWSpinlock is non-reentrant, so recursive acquisition would deadlock.

* [transfer_engine] refactor: narrow RdmaContext endpoint store test surface

Previously exposed a raw EndpointStore* via RdmaContext::endpointStore()
for the integration test. A raw pointer is easy to misuse outside of
tests and couples the caller to the concrete store via dynamic_cast.

Replace with two narrow methods on RdmaContext: waitingListSize() (value
return) and testOnlyInsertWaiting(shared_ptr<RdmaEndPoint>). The latter
is lifted onto the EndpointStore base interface and implemented on both
FIFO and SIEVE, so the integration test no longer downcasts.

* [transfer_engine] test: register endpoint_store_integration_test with ctest

Integration test was previously unregistered and invoked manually. Now
self-skips via GTEST_SKIP when no RDMA device is present, so it runs
cleanly on CI runners without RDMA (skips) and on rxe/mlx5 hosts
(executes). Labeled "rdma" for ctest -L filtering.

* [transfer_engine] perf: short-circuit FIFO reclaim when waiting list is empty

monitorWorker now drives reclaim at ~1 Hz regardless of activity. On FIFO
this grabbed endpoint_map_lock_ as WriteGuard every tick even in the
common steady-state case where waiting_list_ is empty. Add the same
counter-check short-circuit SIEVE already has.

* [transfer_engine] test: skip integration test when RdmaContext::construct fails

GHA ubuntu-22.04 runners enumerate a phantom mlx5_0 via ibv_get_device_list
without a working port/GID, so pickRdmaDevice() returns a non-empty name
and the earlier GTEST_SKIP on empty device list doesn't fire. Then
construct() fails with ERR_CONTEXT and the hard ASSERT_EQ fails the test.

Convert the assertion to a GTEST_SKIP on construct failure. Matches the
"attempt setup, skip on failure" convention used elsewhere in the repo
(e.g., client_local_hot_cache_test.cpp:794-799).
ae292ee837
[transfer_engine] feat: make RDMA QP pkey_index configurable via MC_PKEY_INDEX (#1985)
* [transfer_engine] feat: make RDMA QP pkey_index configurable via MC_PKEY_INDEX

Previously the QP attr.pkey_index was hardcoded to 0 during connection
setup, which prevented use of non-default partition keys. Add a
pkey_index field to GlobalConfig (default 0) that can be overridden
through the MC_PKEY_INDEX environment variable, and apply it when
transitioning the QP to INIT state.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* patch

* add test and also update doc

* Use try catch blocks instead

---------

Co-authored-by: Yan Huang <yan.huang@Yan-Huangs-MacBook-Pro.local>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
16ad1ba92d
[CI] route fork PR to pull_request_target for ascend/integration tests (#1989)
Fork PRs trigger pull_request events where GitHub Actions withholds
vars and secrets. This causes vars.ASCEND_GITHUB_MIRROR_URLS to resolve
as empty on the self-hosted Ascend runner, blocking mirror-based checkout.

Add pull_request_target as a complementary trigger and route both
ascend-test and integration-test by fork origin: non-fork PRs continue
on pull_request, fork PRs are handled by pull_request_target where vars
and secrets are available. Duplicate runs are avoided by skipping the
opposite event for each case.

Signed-off-by: staryxchen <staryxchen@tencent.com>
02d1975ffd
[Store] Unified parallel tensor IO (#1389)
* [Store] add tensor object metadata and TP upsert APIs

Introduce explicit tensor object metadata for tensor read/write paths, add TP-aware tensor upsert wrappers, and update tests/docs for the new serialized layout.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1b6455aa65
[TENT][Sunrise] Add sunrise_link transport, platform support, and UT … (#1915)
* [TENT][Sunrise] Add sunrise_link transport, platform support, and UT coverage

Integrate Sunrise platform/transport wiring across TENT runtime and examples, add SunriseLink end-to-end unit tests, and fix RDMA error logging pointer formatting to avoid crash during registration failure paths.

Made-with: Cursor

* [CI] Update pre-commit hook versions

Bump pre-commit hook revisions to current releases so local checks and CI use newer lint/format toolchains consistently.

Made-with: Cursor

* [TENT][Sunrise] Resolve review issues and drop stale bench target

Address review feedback in SunriseLink transport/platform paths (stream/device context, registration map synchronization, safer probe/allocator handling, and cache-refresh strategy), and remove the obsolete transfer_engine_sunrise_bench CMake target now that its source no longer exists.

* [TENT][sunrise] add engnish doc for sunrise_link

* [TENT][sunrise] restore the memory free logic in the bench, and restore config files

---------

Co-authored-by: liujialai <liujialai@sunrise-ai.com>
f1641d69d2
fix (CI): remove pull_request_target trigger and fork-routing from ci.yml (#1994)
The pull_request_target trigger introduced in PR #1989 was incorrectly
preserved during the merge of PR #1992 due to conflict resolution.
Remove it along with the fork-routing conditions in ascend-test and
integration-test, since the mirror URL is now hardcoded in ci_ascend.yml
and vars/secrets access is no longer needed.

Signed-off-by: staryxchen <staryxchen@tencent.com>
f087c4fdf8
[Store] add ascend dummy real for host mem (#1917)
Co-authored-by: youxiao <youxiao@huawei.com>
ee3e737d9b
[Store] Add Rust smoke test, benchmark, and CI coverage (#1927)
* [Store] Add Rust smoke test, benchmark, and CI coverage


---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>
b0bda8caa4
Exclude ub_transport_test from CI ctest (#2007)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
c19c7b52ee
[Store] Fix LOCAL_MEMCPY segfault for multi-process-per-node deployments (e.g. vLLM data-parallel) (#1995)
* [Store] Fix LOCAL_MEMCPY segfault in multi-process and GPU memory scenarios

Two bugs caused segfaults when MC_STORE_MEMCPY was enabled with
multiple processes on the same node (e.g. vLLM DP8 + mp executor):

1. isLocalTransfer() compared only IPs (stripping ports via
   extractIpAddress), so all processes on the same host were
   incorrectly identified as "local". Cross-process virtual
   addresses are invalid → segfault. Fix: compare full ip:port
   endpoint, restoring the correct behavior from before #1226.

2. workerThread() used bare std::memcpy which cannot operate on
   cudaMalloc device pointers from CPU code → segfault. Fix:
   detect GPU pointers via IsDevicePointer() and use cudaMemcpy
   (cudaMemcpyDefault) for GPU memory, keeping std::memcpy for
   the CPU-only fast path.

Add gpu_staging::CopyAuto() to gpu_staging_utils.h that uses
cudaMemcpyDefault/hipMemcpyDefault for auto-direction copy.

* Modify code format

Signed-off-by: LCAIZJ <leichao139636@163.com>

---------

Signed-off-by: LCAIZJ <leichao139636@163.com>
Co-authored-by: leichao.lc <leichao.lc@antgroup.com>
5ef889dd11
[TENT] Recover cooled-down RDMA rails and add failover e2e tests (#1984)
* feat(tent): improve RailMonitor recovery with exponential backoff and
configurable parameters

- Introduce exponential backoff cooldown with a configurable upper bound
(kMaxCooldown)
- Support dynamic configuration of error threshold, error window, and cooldown
via Config
- Call markRecovered on successful transfer completion to un-pause rails
promptly
- Reset error count and cooldown on recovery to prevent accumulated doubling
- Cache target_machine_id in RdmaSlice to avoid segment lookup on hot path
- Add unit tests for recovery behavior, cooldown reset, and best-device mapping

Signed-off-by: staryxchen <staryxchen@tencent.com>

* test(engine): add end-to-end failover tests and test-only transport swap hook

- Add`swapTransportForTest`method to`TransferEngineImpl`
for test-only transport injection.
- Introduce`engine_failover_e2e_test.cpp`
with scenarios: status corruption failover, budget exhaustion, mixed faults,
per-task independence, and boundary conditions for
`max_failover_attempts`.
- Register new test target`tent_engine_failover_e2e_test`in CMakeLists.

Signed-off-by: staryxchen <staryxchen@tencent.com>

* docs(tent): add failover design doc

Describe TENT's two-layer failure handling: cross-transport failover in
TransferEngineImpl and per-rail cooldown recovery in RailMonitor.
Covers fault model, state machines (with code refs), config knobs,
observability, testing, and known gaps (submit-stage failover, cooldown
reset on recovery, no CI coverage).

Signed-off-by: staryxchen <staryxchen@tencent.com>

* refactor(tent): cache RailMonitor pointer on slice to avoid hot-path string lookup

Each RdmaSlice carried the target machine_id as std::string and
asyncPollCq did a hash+strcmp lookup on worker.rails per completion.
Resolve the RailMonitor once during generatePostPath and stash the
pointer on the slice; the completion path becomes a single deref.

WorkerContext::rails now stores values via unique_ptr so a rehash
only moves the pointer slot and does not invalidate pointers already
held by in-flight slices.

disableEndpoint no longer needs a segment lookup to mark the rail
failed either -- it uses the cached pointer.

Addresses gemini-code-assist review on PR #1984.

Signed-off-by: staryxchen <staryxchen@tencent.com>

---------

Signed-off-by: staryxchen <staryxchen@tencent.com>
043351f048
[TENT] Fix RPC server IPv6 binding on IPv6-only hosts (#2014)
The coro_rpc_server constructor was called with only thread count and
port, defaulting to "0.0.0.0" (IPv4). The ipv6 parameter passed to
CoroRpcAgent::start() was ignored entirely.

On IPv6-only hosts, the server listens on IPv4 while the client
connects via IPv6, resulting in "not connected" / "bad_address" errors
during P2P handshake.

Pass "::" (IPv6 any) or "0.0.0.0" (IPv4 any) based on the ipv6
parameter so the server binds to the correct address family.

Co-authored-by: nishikant <nishikant.kp00@gmail.com>
3d7ce4c47e
[TE] Fix DMA-BUF validation using wrong CUDA device index (#2015)
When WITH_NVIDIA_PEERMEM=OFF, openRdmaDevice() validated DMA-BUF
support by calling cuDeviceGet(&cuDevice, i) where i is the verbs
enumeration index from ibv_get_device_list(). This assumed verbs
device order matches CUDA device order, which is incorrect.

On a GB300 with 10 NICs and 4 GPUs, NICs at verbs index 4-9 called
cuDeviceGet with indices 4-9, which don't exist. This disabled 6 of
10 NICs with "Failed to query CUDA device", limiting RDMA throughput
to 4 NICs (18 GB/s) instead of all 10 (41 GB/s).

Fix:
- Look up the topology matrix to find which CUDA devices list this
  RNIC in their preferred_hca or avail_hca, then validate DMA-BUF
  only for those specific devices.
- Check avail_hca in addition to preferred_hca because runtime NIC
  selection can fall back to avail_hca after disableDevice() removes
  a failed preferred NIC. Without this, the fallback NIC may not have
  been validated for the GPU that ends up using it.
- Add explicit cuInit(0) before cuDeviceGet. The old code relied on
  implicit CUDA driver initialization from a prior cudaGetDeviceCount
  call in topology discovery. Making it explicit removes a fragile
  ordering dependency.

Validation on GB300 (single-host VRAM read, GPU 1 -> GPU 0, all 10
NICs auto-discovered):

    Topology discovery complete. Found 10 HCAs.
    RDMA device: mlx5_0 ... mlx5_9  [all 10 register, GIDs elided]

  Pre-fix only the 4 NICs at verbs index 0-3 would have passed
  validation; mlx5_4-9 would have been disabled with "Failed to
  query CUDA device". With the fix, the bench reaches 41 GB/s
  saturation (see follow-up topology commit) instead of being
  capped at ~18 GB/s.

Co-authored-by: nishikant <nishikant.kp00@gmail.com>
aa2244c283
[TE] Save the active device with hipGetDevice on entry and restore it before (#2018)
* Save the active device with hipGetDevice on entry and restore it before
returning. The function is now transparent to the caller's HIP context.
c02b669669
[TE] Fix EFA segfault race and DP>1 peer_map_ thrashing (#2023)
* Fix segfault race between setPeerNicPath and in-flight submitPostSend

EfaEndPoint::submitPostSend was latching peer_fi_addr_ under lock_ and
releasing the lock before calling EfaContext::submitSlicesOnPeer, which
calls fi_write(shared_ep_, ..., peer_fi_addr, ...) outside the lock. A
concurrent setPeerNicPath() / disconnect() (both take the write lock and
call context_.removePeerAddr, i.e. fi_av_remove) could invalidate the
AV slot between the latch and the fi_write, causing libfabric's EFA
provider to dereference a stale entry and segfault.

Repro: SGLang 2P2D GLM-5.1-FP8 prefill, PP=2 TP=8 CP=8 EP=8 topology,
moe_a2a=deepep, 16 EFA NICs, MC_MAX_WR=16384. The P2P handshake port
changes for a peer process after a rebind, setPeerNicPath() takes the
CONNECTED branch and disconnects while sender threads have fi_write
calls in flight for that peer.

Fix: hold the read lock across the entire submitSlicesOnPeer call so
disconnect (write lock) cannot remove the AV entry while a submit is
in flight. Re-check status_ under the lock and fail the batch if a
disconnect raced in between, letting the caller retry with a fresh
setupConnectionsByActive(). Read locks still allow multiple senders to
the same peer to submit in parallel, so no throughput regression.

Refs: https://github.com/kvcache-ai/Mooncake/issues/2022

* fix(efa): skip AV churn on duplicate passive handshake, silence "Re-establish" warnings

Under bilateral sglang P/D traffic every (localNIC, remoteNIC) pair sees two handshakes: the active side inserts the peer into the AV, and the remote side later fires its own active handshake which arrives at our passive handler after we are already CONNECTED. The previous code unconditionally treated that second handshake as a reconnect, logged W "Re-establish EFA connection" and ran fi_av_remove + fi_av_insert. Observed impact on a real run: ~tens of thousands of lines per decode node, and a brief window where in-flight fi_write could see a stale fi_addr_t (the race PR 2023 already hardened against on the submit side).

Cache the EFA address bytes that we successfully inserted. On a passive handshake, if we are CONNECTED and the incoming peer address matches the cache, it is the same peer and the same AV slot — return the local desc and skip the churn entirely. Only reinsert if the peer address genuinely changed (peer process restart → new QPN → different bytes); demote that branch to LOG(INFO) so real reconnects remain visible without being drowned out.

Cache is populated from both active and passive connect paths and cleared in disconnectUnlocked / markDetachedForTeardown so a subsequent passive handshake takes the first-connect path correctly.

Complements PR 2023: PR 2023 holds the read lock across submitPostSend so the AV cannot be removed mid-fi_write; this change removes the AV churn at the source for 99% of the handshakes, so the read-lock hold becomes defense-in-depth rather than the only barrier.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(efa): don't skip fi_av_insert on duplicate passive handshake

Previous commit skipped fi_av_remove + fi_av_insert when the passive peer address matched the cached one. Under bilateral sglang P/D load (128-concurrency, 3K bench) this caused requests to stall: 31 transfers stuck in #transfer-req, prefill #inflight-req pinned at 128 with no progress. The hypothesis that EFA handshakes with an unchanged peer address could be treated as a no-op was wrong — libfabric's EFA provider tracks per-peer transport state (AH activation, RNR backoff, internal sequence bookkeeping) that a fresh fi_av_insert re-seeds, and skipping it leaves the provider in a stale state that silently drops or stalls the fi_write path.

Keep the cached_peer_addr_ field (it's cheap and harmless) but drop the AV-skip fast path. The reinsert now happens on every passive handshake, restoring the functional behavior PR 2023 alone validated. The only surviving optimization is log-level classification: same-address handshake → VLOG(1), different-address → LOG(WARNING). This still eliminates the "Re-establish EFA connection" warning spam on bilateral symmetric traffic without touching semantics.

PR 2023's read-lock widening in submitPostSend is now the primary defense for the race described there, with the AV reinsert once again the common-case path. Verified by repro: sglang-glm5-2p2d bench 3072 1024 500 18 128 no longer stalls.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(efa): don't tear down AV slot when only the peer RPC port rotates

Symptom: after the previous commit (which restored fi_av_insert on every passive handshake) traffic no longer stalls, but decode logs still show a flood of "Peer reconnected with new address, re-establishing" from setPeerNicPath. Each of those lines corresponds to a peer whose EFA endpoint did not move — only the P2PHANDSHAKE RPC port changed.

Root cause: peer_nic_path_ stores the full "host:PORT@nic" string. Every sglang KV transfer bootstrap picks a new RPC port, so setPeerNicPath sees a "different" path and calls disconnectUnlocked() + reinserts, even though the EFA peer address is identical. The RPC port is pure handshake metadata — EFA SRD addressing is keyed on the binary efa_addr (GID/QPN) returned by fi_getname(), which is stable for the lifetime of the peer process. The spurious AV churn pays a fi_av_remove + fi_av_insert plus an AH activation warm-up (first-packet latency hit) on every transfer, exactly the performance impact the user was concerned about.

Fix: in setPeerNicPath, compare the normalized nic path (host+NIC, no port) before tearing anything down. If only the port changed, update the stored string and return; keep peer_fi_addr_ and cached_peer_addr_ intact so the next submitPostSend can reuse the already-active AH. A genuine peer restart is still caught downstream by setupConnectionsByPassive's efa_addr == cached_peer_addr_ check (peer QPN differs after restart → different bytes → real re-establish path).

Also normalize the path equality check in setupConnectionsByPassive's sanity guard so the same port rotation doesn't spuriously reject the handshake.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Revert "fix(efa): don't tear down AV slot when only the peer RPC port rotates"

This reverts commit 4548bf56aaf4ac07f3c98b5434a0ab613874fed5.

* fix(efa): key peer_map_ by full host:port@nic to handle multi-worker peers

Root cause of the "Peer reconnected" spam (and the AH warm-up tax on every KV transfer) in sglang 2P2D with DP>1.

EfaContext::endpoint() was keying peer_map_ by normalizeNicPath(peer) which strips the RPC port. The design comment said this was to "reuse handles across reconnections" since each P2PHANDSHAKE init picks a random port. That premise assumed one Mooncake TransferEngine per peer host — true for DP=1 benchmarks but violated under sglang P/D disaggregation: each DP worker is a separate Python process with its own TransferEngine and its own rpc_port, and they all share the same host IP + EFA NIC on that host.

With DP=N peer workers, all N of them collapse onto a single peer_map_ slot. Every incoming handshake from a different DP worker looks to that slot like "peer reconnected with a new address" (different port), so setPeerNicPath() tears down the AV slot (fi_av_remove + fi_av_insert) and the next fi_write pays an AH warm-up. Under bilateral 128-concurrency traffic this devolves into permanent thrashing; the symptoms reported by the user track this model precisely:

- DP=1: no spam, normal performance (single worker per host, normalization harmless).
- DP=2/4: increasing Re-establish log volume, progressive first-packet latency.
- DP=8: thousands of Re-establish per second, decode #running drops to 1, prefill #inflight stays pinned at 128, timeouts.

Fix: key peer_map_ by the full "host:port@nic" path. Each DP worker is a stable, distinct process with a stable port for its entire lifetime, so the cache still hits on every steady-state lookup — no churn. A genuine peer process restart (new rpc_port) leaves the old EfaEndPoint in the map; that is a few bytes of leak per ex-worker, far cheaper than the per-transfer AV teardown it replaces. peekEndpoint, deleteEndpoint, and warmupSegment's post-failure cleanup all switch to the full path for consistency. setPeerNicPath() stays intact as defensive code but will no longer fire in the common path because each new DP worker now gets its own endpoint.

Undoes the buggy 4548bf5 from a completely different direction: the previous attempt tried to skip AV churn at the setPeerNicPath level, which hid real peer restarts and corrupted rkey routing. The correct fix is upstream of that — separate endpoints per peer process so port changes only happen on actual restart.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* style(efa): clang-format-20 fixup

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: EC2 Default User <ec2-user@ip-172-31-8-212.us-west-1.compute.internal>
3ac7de25ac
[Store] Add lock-free MmapArena allocator for buffer mmap path (#1820)
* [Store] Add lock-free MmapArena allocator for buffer mmap path

Replace per-allocation mmap() syscalls in allocate_buffer_mmap_memory()
with a lock-free atomic bump allocator (MmapArena). Pre-allocates a
configurable pool (default 64GB) and serves allocations via CAS loop,
reducing allocation latency from ~1us (mmap syscall) to ~50ns (atomic).

Allocation lifecycle is static: all callers (ClientBufferAllocator,
global segments in RealClient::setup_internal) allocate at startup and
free at shutdown. The arena outlives all allocations, so the bump-only
(no individual free) design is correct for this usage pattern.

Feature-flagged via gflags:
  --use_mmap_arena_allocator (default: true)
  --mmap_arena_pool_size (default: 64GB)

Falls back to direct mmap() when arena is disabled, fails to init,
or is exhausted.

Cherry-picked from flow-ipc-poc branch (utils.cpp perf path only).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] Fix three correctness issues in MmapArena

1. Honor caller's alignment contract: allocate() now accepts a
   per-call alignment parameter and uses max(arena default, caller
   request). allocate_buffer_mmap_memory() forwards its alignment
   argument to the arena. Previously, the caller's alignment was
   silently ignored — the arena always used 64-byte alignment
   regardless of what the caller requested.

2. Remove MAP_POPULATE from arena pool mmap: the default pool is
   64GB but callers typically use only a fraction (e.g. 4GB of
   segments). MAP_POPULATE would pre-fault all 64GB of pages upfront,
   causing seconds of startup delay and potentially triggering OOM
   on machines with less physical memory. Pages now fault on demand.

3. Make alignment_ atomic and store it BEFORE the CAS on pool_base_:
   previously alignment_ was a plain size_t written AFTER the release
   CAS, so the store was not in the happens-before relationship
   established by the acquire-release pair on pool_base_. Now both
   alignment_ and pool_size_ are stored before the CAS with the
   release fence guaranteeing their visibility to readers.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
dd9f8ae004
[Store] Allow customizing client port range (#2008)
* [ADD] MC_STORE_CLIENT_MIN_PORT and MC_STORE_CLIENT_MAX_PORT environ

* [UPD] documentation

---------

Co-authored-by: Copilot <copilot@github.com>
b5ded0c4d2
[Doc] Update README news: add Apr 29 2026 lmsys P2P weight transfer blog (#2038)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
7c62d29b11
refactor: unify fabric allocator plumbing (#2028)
Share allocator build scaffolding across nvlink and ubshmem backends.

- expose a common allocator probe/malloc/free ABI while keeping legacy symbols
- reuse shared Python helper logic for allocator loading and probing
- centralize allocator CMake and shell build helpers for packaging and builds
2a5a94a030
[TE]: Fix possible dead lock in RDMA transport connection setup (#1959)
Say we have 3 transfer engine instances T0, T1, T2, in the following
case with P2PHANDSHAKE, they will form a circular dead lock:

T0.listener is handling connection request from T1:
  -> setupConnectionsByPassive()
    -> getSegmentDescByName(T1)
      -> exchangeMetadata(T1)
      -> wait for T1.listener processing

T1.listener is handling connection request from T2:
  -> setupConnectionsByPassive()
    -> getSegmentDescByName(T2)
      -> exchangeMetadata(T2)
      -> wait for T2.listener processing

T2.listener is handling connection request from T0:
  -> setupConnectionsByPassive()
    -> getSegmentDescByName(T0)
      -> exchangeMetadata(T0)
      -> wait for T0.listener processing

T0 -> T1 -> T2 -> T0

To fix this, we can remove the calling to getSegmentDescByName
completely from the connection establish process, and exchange
necessary connection information through HandShakeDesc. This can
also significantly simplify the connection process.

Signed-off-by: Chen Jinlong <chenjinlong.cjl@alibaba-inc.com>
658297c4d9
[TransferEngine] Use allocation base addr for dmabuf-based mem registration (#2035)
---------

Signed-off-by: Yifan Qiao <yifanqiao@inferact.ai>
83ccd8a39f
[PG] Inherit ProcessGroup to fix dynamic getSize() after extend_group_size_to (#2040)
* [PG] Inherit ProcessGroup to fix dynamic getSize() after extend_group_size_to

MooncakeBackend now inherits c10d::ProcessGroup instead of c10d::Backend,
so overriding getSize() actually takes effect (ProcessGroup::getSize() is
virtual; Backend::getSize() is not).

Key changes:
- MooncakeBackend: base class ProcessGroup(store, rank, size); override
  getSize() returns meta_->size (dynamic) instead of the static size_
- MooncakeBackendOptions: inherits torch::CustomClassHolder instead of
  Backend::Options (Backend-specific base no longer applicable)
- pg_py.cpp: factory functions return intrusive_ptr<ProcessGroup>;
  helper functions (extend_group_size_to, get_peer_state, etc.) accept
  intrusive_ptr<ProcessGroup> and downcast to MooncakeBackend
- pg_test_utils.py: get_mooncake_backend() returns the group directly
  (no _get_backend() call); removed device_id from init_mooncake_group
  to avoid PyTorch calling pg._get_backend() post-init for ProcessGroup
  subclasses with no registered backend
- mooncake_ep_buffer.py: self.backend = self.group (same reason)
- test_pg_elastic.py: add test_dynamic_world_size verifying
  dist.get_world_size() returns N+1 after extend_group_size_to(N+1)
- test_mooncake_backend_elastic.py: assert dist.get_world_size() after
  extension; use dist.group.WORLD directly as backend handle

PyTorch shortcut: _new_process_group_helper checks
issubclass(type(backend_class), ProcessGroup) — when true, the returned
instance is used directly as the PG without _register_backend, so
_get_backend() is no longer needed or valid.

Verified on k8s (sunxun/mooncake-pg-process-group-test, 1 GPU):
- 18/18 CPU collective tests pass (test_pg_collectives.py -k CPU)
- test_dynamic_world_size passes: world_size correctly updated 4 -> 5
- test_elastic_extension passes
CUDA tests deferred: allreduce hang on available pod is pre-existing
(upstream main has same behavior; RDMA not properly configured there).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* [PG] Fix clang-format violations and apply reviewer suggestions

- mooncake_backend.h: collapse two-line initializer list to one line;
  collapse getSize() body to single-line inline form (Google style)
- pg_py.cpp: wrap extendGroupSizeTo signature at 80 cols
- mooncake_backend.cpp: std::move distBackendOpts.store to avoid
  unnecessary intrusive_ptr ref-count increment (reviewer suggestion)
- test_pg_elastic.py: drop unused `device` binding in
  _dynamic_world_size_worker (CodeQL unused-local warning)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
44cde29c84
[TE] fix(efa): request libfabric API 1.18 so device RDMA is the default on all EFA generations (#2041)
Mooncake's fi_getinfo() requests FI_VERSION(1, 14). The EFA provider's efa_rdm_get_use_device_rdma() keeps a legacy compatibility branch for callers on API < 1.18 that hardcodes the default for FI_EFA_USE_DEVICE_RDMA based on vendor_part_id: efa0/efa1 → false, everything newer → true. Under that legacy branch we silently disabled device RDMA on Nitro v4 EFA hardware (p5.48xlarge, p5e.48xlarge — vendor_part_id 0xefa1) while leaving it enabled on Nitro v5+ (p5en and later). Cross-node transfer_engine_bench reproduces the split: p5en runs fine out of the box, p5/p5e segfault inside libfabric.so during fi_cq_read once the handshake wave finishes and the first real fi_writes start. The crashing stack has a concurrent fi_av_insert in flight on the same EfaContext, and the crashing memcpy is in the provider's emulated-RDMA CQE reconstruction path — i.e. a thread-safety regression in libfabric 2.4.0's emulated RDMA data path that only ever runs when device RDMA is off.

Request API 1.18. With the 1.18+ code path the default becomes hw_support (unconditionally true on every EFA hardware that supports RDMA, which is every Mooncake target platform starting from p4d), so p5/p5e pick up the same device-RDMA default p5en already has, the emulated path is never entered, and the segfault is gone. Applications that still want the emulated path can opt out with FI_EFA_USE_DEVICE_RDMA=0. 1.18 is from March 2023 and is the oldest libfabric shipped with EFA installer 1.26 and up; every Mooncake EFA deployment today runs libfabric ≥ 2.0, so bumping the requested API costs nothing in compatibility.

Verified by transfer_engine_bench on two p5.48xlarge nodes (libfabric 2.4.0amzn1.0, 32 EFA NICs, 8 × H100 per node):
- Before this patch, no env: target SIGSEGV inside libfabric.so on first real transfer.
- Before this patch, FI_EFA_USE_DEVICE_RDMA=1: 377.74 GB/s, stable.
- After this patch, no env: 377.93 GB/s, stable — matches the explicit env case.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
ea8fa5dad9
[TE] fix rdma race (#1903)
Signed-off-by: Tianchen Ding <dtcccc@linux.alibaba.com>
c3bafbedd1
[PG] Fix P2P regression caused by ProcessGroup inheritance (#2043)
* [PG] Fix P2P regression by registering Backend shim in ProcessGroup

After PR #2040 changed MooncakeBackend to inherit from ProcessGroup
instead of Backend, PyTorch's P2P dispatch path (batch_isend_irecv,
isend, irecv) broke because getBackend() could not find a registered
Backend instance in the ProcessGroup's deviceTypeToBackend_ map.

Introduce MooncakeP2PShim, a lightweight Backend subclass that holds a
non-owning pointer back to its owning MooncakeBackend and delegates
send/recv operations. The shim is registered in the constructor via
setBackend() so that _get_backend()/getBackend() lookups succeed.

* [PG] Forward-declare MooncakeBackend to fix CUDA compilation

The MooncakeP2PShim constructor in the header referenced
MooncakeBackend before it was defined.  Move the constructor
definition to the .cpp and add a forward declaration.

* [PG] Set default backend type to CUSTOM in ProcessGroup

Without setDefaultBackend(CUSTOM), hasHooks() looks up backendType_
which defaults to UNDEFINED (0), producing a noisy warning on every
P2P operation.

* [PG] Clean up MooncakeP2PShim: remove unused deviceType_ and barrier dummy tensor

- Remove deviceType_ member: it was stored but never used in any method.
- Remove unused dummy tensor allocation in barrier(): it was created but
  never passed to the underlying barrier call.

* [PG] Mark MooncakeP2PShim constructor explicit

* [PG] Fix clang-format: split getBackendName return type to satisfy ColumnLimit 80

* [PG] Fix clang-format: single-line getBackendName (80 cols exactly)
84df45a906
[PG] Refactor P2PProxy with shared chunk pools and receiver-driven credit-based flow control (#1971)
* [PG] Refactor P2P: Chunk pool + Receiver-driven pipeline

* [PG] Refactor P2P State Machine for better readability.

* [PG] Per-peer generation and better failure handling.

* [PG] Parse pool configuration from environment.

* fix typos and refine comments.

* apply gemini-code-assist's suggestions.

* [PG] Share P2PChunkPool across backends.

* [PG] Fix poor naming and address gemini's review comments.
23e114661b
docs: add vLLM Mooncake Store blog post to README updates (#2052)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
43ce0f8ff5
[Tent][AMD] Add AMD CDNA4 (ROCm/HIP) platform support with benchmark tooling (#2021)
---------

Co-authored-by: root <root@smci355-ccs-aus-n06-05.cs-aus.dcgpu>
dec5ab40b7
[Store] fix(master-metrics): track local SSD storage in Master metrics (#1969)
* fix(master-metrics): track local SSD storage in Master metrics

Master logs always showed "SSD Storage: 0 B / 0 B" even when local SSD
offloading was active. Three root causes fixed:

Bug 1 — allocated size never tracked for LocalDiskReplica (replica.h)
  The LocalDiskReplica constructor did not call inc_allocated_file_size(),
  so the numerator stayed zero regardless of how many objects were offloaded.
  Symmetric fixes applied to the destructor and move-assignment operator.
  Also fixed a pre-existing bug: id_ and refcnt_ were uninitialized in the
  LocalDiskReplica constructor.

Bug 2 — total SSD capacity unknown to Master
  Master has no direct visibility into client-side SSD configuration, so
  file_total_capacity_ (the denominator) was always 0. A new dedicated RPC
  ReportSsdCapacity(client_id, ssd_total_capacity_bytes) is added; clients
  call it once in FileStorage::Init() after MountLocalDiskSegment succeeds.
  Master stores the value per-client in LocalDiskSegment::ssd_total_capacity_bytes
  and updates MasterMetricManager via inc/dec on change. Old clients that
  lack this RPC simply never call it — OffloadObjectHeartbeat signature is
  unchanged, ensuring backward compatibility.

Bug 3 — data race in UnmountLocalDiskSegment (segment.cpp)
  ssd_total_capacity_bytes was read without holding offloading_mutex_ while
  OffloadObjectHeartbeat writes it under that lock (C++ UB). Fixed by reading
  inside a scoped lock block, then releasing the lock before erase() to avoid
  unlocking an already-destroyed mutex.

* Update mooncake-store/src/master_service.cpp

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
32471c9b9e
[Doc] feat: readme hardware (#2053)
* [Misc] Add hardware summary and Copilot worktree hook

* [Doc] Polish hardware support section and drop worktree hook

* [Doc] Replace generated hardware badges with real logos

* [Doc] Refine hardware support matrix

* [Doc] Restore vendor icon strip
b34fd159b8
[TENT] Add reference counting to RdmaTask to prevent UAF (#2047)
* Add reference counting to RdmaTask to prevent UAF

- Convert RdmaSubBatch::task_list from value to pointer storage
- Add atomic reference counting to RdmaTask with Slab allocator integration
- Properly dereference tasks in freeSubBatch cleanup path
- Each slice holds a reference to its parent task

Author: Feng Ren <alogfans@gmail.com>

* Add paired ref/deref

* remove task->ref_count assignment
4326adbd51
[engram] support engram (#1483)
* support engram

Signed-off-by: Cruz Zhao <CruzZhao@linux.alibaba.com>

* add test case for engram

Signed-off-by: Cruz Zhao <CruzZhao@linux.alibaba.com>

* add docs for engram

Signed-off-by: Cruz Zhao <CruzZhao@linux.alibaba.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
22fd38dabb
[TE] feat(transport): add independent maca_transport for Metax MACA C500 (#2059)
This commit introduces a standalone maca_transport following the same
pattern as hip_transport, instead of polluting nvlink_transport with
MACA-specific workarounds.

Key points:
- Sync mcMemcpy with device-context guard (save/restore device before
  each copy to avoid mcErrorContextIsDestroyed / SIGSEGV).
- Base-pointer registration via cuMemGetAddressRange for correct IPC
  handle semantics with framework caching allocators.
- IPC-only path; fabric memory is not reliably supported on MACA 3.5.3.
- P2P access enabled in constructor with original device restoration.

Glue changes:
- multi_transport.cpp: register "maca" protocol
- transfer_engine_impl.cpp: auto-install maca transport under USE_MACA
- transfer_metadata.cpp: add "maca" to encode/decode protocol whitelist
- transfer_engine_validator.cpp: support --protocol=maca
- maca.h: add missing CU_POINTER_ATTRIBUTE_*, cuGetErrorString macros

Verified on Metax C500 (2-GPU) with transfer_engine_validator:
  Data validation passed, throughput ~6.9 GB/s
879135177c
[tebench] support nvlink xport and register buffers per-allocation (#2073)
for shm transport, shm_path created by allocateLocalMemory should be passed into registerLocalMemory

Co-authored-by: jinke15 <jinke15@jd.com>
93f49168d3
[TENT] fallback to per-task cudaMemcpyAsync when driver lacks batch s… (#2072)
* [TENT] fallback to per-task cudaMemcpyAsync when driver lacks batch support

Fixes the case where containers ship CUDA Toolkit 12.8 but the host
driver only supports 12.2

* remove unconditionally overwrites the err

* style: format nvlink_transport.cpp with clang-format-20

---------

Co-authored-by: jinke15 <jinke15@jd.com>
c3fab08473
[TransferEngine] Fix GPU dependency in transfer_engine_bench (#2068)
* [TransferEngine] Fix GPU dependency in transfer_engine_bench

Problem: transfer_engine_bench crashes (exit code 247) when running with
--use_vram=false in environments without GPU, even though it only uses
CPU memory (DRAM).

Root cause: freeMemoryPool() calls cudaPointerGetAttributes() with
checkCudaError(), which exits the program if CUDA fails.

Solution: Use graceful error handling like transfer engine core library
(memory_location.cpp). When CUDA query fails, assume CPU memory and
use numa_free().

Impact:
- Enables CPU-only RDMA bandwidth testing without GPU
- Consistent behavior with mooncake_client
- No impact on existing GPU-enabled scenarios

Test: Verified RDMA bandwidth testing works in CPU-only pods and
achieves 10+ GB/s throughput on 200G RDMA network.

Signed-off-by: jibxie <jibxie@ebay.com>

* [TransferEngine] Optimize memory deallocation logic in transfer_engine_bench

Check FLAGS_use_vram before calling cudaPointerGetAttributes to avoid
unnecessary CUDA calls when memory is explicitly allocated on CPU.

- When FLAGS_use_vram is false, memory is guaranteed to be allocated
  via numa_alloc_onnode, so we can directly call numa_free without
  checking CUDA pointer attributes
- This avoids confusing WARNING logs on systems without GPU when users
  explicitly choose to use DRAM
- Change log level from WARNING to ERROR when FLAGS_use_vram is true
  but cudaPointerGetAttributes fails, for consistency with
  memory_location.cpp

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>

---------

Signed-off-by: jibxie <jibxie@ebay.com>
Co-authored-by: jibxie <jibxie@ebay.com>
Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
6caf41289d
[TransferEngine][Integration] feat: add MACA/MetaX GPU support and fix RDMA dmabuf registration (#2019)
* [TransferEngine][Integration] feat: add MACA/MetaX GPU support and fix RDMA dmabuf registration

- Fix MACA compatibility macros: correct CUdeviceptr alias, add missing
  CUDA memory type and pointer attribute macros, implement inline
  cuGetErrorString wrapper
- Fix RDMA dmabuf memory registration for GPU memory: use allocation
  base address for cuMemGetHandleForAddressRange and compute proper
  offset for ibv_reg_dmabuf_mr (fixes #1975, #1965)
- Add USE_MACA guard alongside existing USE_MLU/USE_CUDA guards for
  dmabuf-based memory registration path
- Support remote_request_id in mooncake connector v1 for cross-request
  KV cache transfer between prefiller and decoder

---------

Co-authored-by: zhangxin <zhangxin@zhangxins-MacBook-Air.local>
ef0f40cd2d
[Store] Fix SpinLock memory order for weakly-ordered architectures (#2076)
The SpinLock::lock() inner spin loop used memory_order_relaxed for
flag.test(), which can fail to promptly observe unlock() on ARM/RISC-V.

- Use memory_order_acquire on test_and_set in both fast and slow paths
- Keep relaxed loads only inside the inner PAUSE() spin loop for performance
- This ensures proper happens-before synchronization on weakly-ordered CPUs
704da4b92f
[Store][Fix]: enable local memcpy for metadata local replicas (#2029)
* fix(store): enable local memcpy for metadata local replicas

Pass the client local hostname into TransferSubmitter and use it when
detecting local transfers. This allows metadata-service descriptors, which
use the hostname as the segment identifier, to correctly select LOCAL_MEMCPY
for local reads while preserving transfer-engine endpoint matching for P2P
descriptors.

Add coverage for TCP local memcpy auto-enable behavior across P2P and
metadata modes, including remote same-host cases that should continue using
TRANSFER_ENGINE. Also verify hot-cache hits do not increment the admission
sketch when LOCAL_MEMCPY is selected.
da9dfea387
[Store] Expose is_local_disk_replica() to Python + enable offload RPC in standalone mooncake_client (#2083)
* [Store] Expose is_local_disk_replica() to Python in ReplicaDescriptor

Replica::Descriptor is a 3-way std::variant {MemoryDescriptor,
DiskDescriptor, LocalDiskDescriptor} with a corresponding C++ predicate
per type. The Python wrapper was missing is_local_disk_replica.

Mooncake's offload pipeline (NotifyOffloadSuccess) constructs
LocalDiskDescriptor exclusively. With only is_memory_replica /
is_disk_replica exposed to Python, every LOCAL_DISK descriptor returned
by the master to a Python caller would test False on both predicates
and be misclassified (e.g. as "unknown" in tier diagnostics) regardless
of whether the actual load succeeded.

---------

Co-authored-by: Zhewen Li <zhewenli@inferact.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
253d889b68
[Store] add SSD-offload support for ascend platform (#2005)
Co-authored-by: youxiao <youxiao@huawei.com>
b0ae4a727f
[Store] Add ObjectDataType enum for type-aware metadata (#1719)
* feat: add ObjectDataType enum and metadata propagation (Phase 1 of #1673)

Introduce a data type classification system for objects stored in
Mooncake Store, as agreed in the RFC discussion on issue #1673.

Changes:
- Add ObjectDataType enum (UNKNOWN, KVCACHE, TENSOR, WEIGHT, etc.)
  in mooncake-store/include/types.h
- Extend ReplicateConfig with a data_type field (default UNKNOWN)
- Propagate data_type through PutStart into ObjectMetadata
- Serialize/deserialize data_type in snapshot metadata, with backward
  compatibility for old snapshots (gracefully handles missing field)
- Expose ObjectDataType enum and data_type field in Python bindings
- Add unit tests for enum values, defaults, and PutStart propagation

Existing clients that don't set data_type will continue to work
unchanged (defaults to UNKNOWN everywhere).

---------

Co-authored-by: Yufeng He <40085740+universeplayer@users.noreply.github.com>
368b41f168
[Docs] Tag 3fs Feature as Experimental (#2062)
* Update 3fs docs

* Update news

---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
a24413a1b2
[Doc] Fix the inconsistent param description (#2103)
---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
377dcba3fb
[Store] Add comprehensive test suite for Python binding error handling (#2097)
Add 20+ C++ tests and 40+ Python tests covering error handling,
corner cases, and previously untested API endpoints for the
mooncake-store Python binding layer.

C++ tests (pybind_client_test.cpp):
- batchIsExist with mixed existing/missing keys and before setup
- getSize for existing keys and before setup
- removeByRegex with matching, non-matching, and pre-setup cases
- removeAll on empty store
- batchRemove with valid, non-existent, and pre-setup keys
- put_parts and put_batch then batch_get_buffer roundtrip
- health_check after setup and before initialization
- get_hostname verification
- Double tearDownAll idempotency
- Empty batch operations
- Mount non-existent file, unmount invalid segment IDs

Python tests (test_mooncake_store_service_api.py):
- /api/put: success, missing key/value, empty key, store failure
- /api/get: success, not found, empty bytes, store exception
- /api/exist: true, false, store exception
- /api/remove: success, failure, store exception
- /api/remove_all: success, zero keys, failure, store exception
- /api/reconfigure: decode/prefill success, missing params, invalid mode,
  remount, unmount failure rollback
- /api/mount: negative/float/string/missing size
- /api/unmount: missing segment_ids, empty list, success
- /api/mount_shm: store failure, defaults, invalid names
- /api/unmount_shm: string coercion, empty list
- _shm_name_to_path: 11 unit tests for path validation edge cases

Closes #654

Co-authored-by: yurekami <yurekami@users.noreply.github.com>
8eb0e2e5d1
[CI] pin torch version to 2.11.0 (#2105)
PyTorch 2.12.0 does not natively support CUDA 12.8. Patching around this will make our CI script fragmented.
9128a63ed9
[PG] update EP/PG torch versions — drop 2.9.0, add 2.12.0 (#2101)
PyTorch 2.12.0 no longer publishes cu128 wheels (only cu130 and cu126).
Add a cmake branch in SetupPyTorchEnv.cmake that routes torch 2.12.0+
on CUDA 12 hosts to the cu126 wheel index.

Changes:
- EP_TORCH_VERSIONS: 2.9.0;2.9.1;2.10.0;2.11.0 → 2.9.1;2.10.0;2.11.0;2.12.0
- SetupPyTorchEnv.cmake: add version >= 2.12.0 branch → cu126
fdf95f9565
[Store][K8s-Native][2/N] K8s leader election (#1956)
---------

Signed-off-by: Vladislav Nosivskoy <vladnosiv@gmail.com>
6ab98c7aca
[TE] Expose sendProbe via Python binding (#2088)
Exposes the existing TransferMetadata::sendProbe C++ method through the
TransferEngine pybind module as engine.send_probe(peer_server_name).
This enables SGLang's MooncakeKVManager to issue lightweight JSON-RPC
probes against peers, used to test whether a previously-blacklisted
mooncake_session_id has become reachable again so it can be removed
from the failed_sessions set.

Returns 0 on success, non-zero on failure (matching the C++ contract).
No behavior change for existing engine.* methods.

Tested:
- New Python unit tests in transfer_engine_initiator_test.py covering
  both the reachable-peer and unknown-peer cases.
- Manually validated end-to-end against SGLang's MooncakeKVManager.
c9be4e68f0
[Store] fix: prevent cross-process memcpy segfault when MC_STORE_MEMCPY auto-enables (#2001)
* [Store] fix: require same-process endpoint for LOCAL_MEMCPY strategy

isLocalTransfer compared only the IP of handle.transport_endpoint_ to
the local endpoint, so two processes on the same host (same IP, different
ports) were treated as LOCAL_MEMCPY-eligible. The memcpy worker then
dereferenced handle.buffer_address_, which is a virtual address only valid
in the owning process, and segfaulted inside __memcpy_avx512_unaligned_erms.

This was latent before #1936 (MC_STORE_MEMCPY defaulted to off). The
TCP-only auto-enable exposed it on multi-process workloads such as the
TorchSpec inference/trainer pipeline.

Compare the full transport endpoint instead, matching the check already
used by Client::IsReplicaOnLocalMemory. Cross-process same-host transfers
now correctly fall through to TRANSFER_ENGINE; same-process transfers
still take the memcpy fast path.

Fixes the crash reported with MC_STORE_MEMCPY auto-enabled on TCP-only hosts.

---------

Co-authored-by: Teng Ma <teng-ma@linux.alibaba.com>
53480ac176
[TENT] Fix batch getTransferStatus premature FAILED aggregation (#2055)
* [TENT] Fix batch getTransferStatus premature FAILED aggregation

Previously, one permanently-FAILED task would latch overall_status to
FAILED even while other tasks were still PENDING (mid-failover). This
caused lazyFreeBatch to teardown the batch while retries were in-flight.

Now the batch reports FAILED only when ALL tasks reach a terminal state
(success_tasks + failed_tasks == total_tasks). A task still in PENDING
(e.g. resubmitted on a secondary transport) keeps the batch in PENDING.

Add unit tests covering the new aggregation logic: FAILED+PENDING →
PENDING, FAILED+COMPLETED → FAILED, all COMPLETED → COMPLETED, and
derived-task skipping.

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>

* [TENT] Replace hardcoded transport type tests with dynamic sentinel check

The old AllEnumValuesDistinct and SupportedCount tests hardcoded the
enum list and expected count, breaking whenever a new transport type
(like SUNRISE_LINK) was added. Replace them with UnspecIsSentinel
which verifies invariants independent of how many transports exist.

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>

* style(test): reformat lambda expression in failover_test

- Adjust line break for lambda assignment to improve readability

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>

* fix(transfer-engine): correct worst failure tracking in getTransferStatus

- Introduce severity-based comparison to ensure `worst_failure` reflects the most severe status, preventing overwrites with lower severity.

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>

---------

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>
Co-authored-by: Yuxin Chen <grityxchen@gmail.com>
d59458172a
[Store] fix: mooncake_master -version prints release version and git commit hash (#2110)
---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>
26b24565ff
[PG] Fix scale-up semantics with two-phase extension protocol (#1968)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
218f2ffdcf
[] feat: add Hygon DCU/DTK and Iluvatar CoreX platform support (#2118)
* feat: add Hygon DCU/DTK and Iluvatar CoreX platform support

Add build system and runtime support for two CUDA-compatible domestic
accelerator platforms:

- Hygon DCU with DTK SDK (USE_HYGON, /opt/dtk/cuda/cuda-11/)
- Iluvatar CoreX SDK (USE_COREX, /usr/local/corex/)

Both platforms expose CUDA-compatible APIs, so the integration follows
the same pattern as existing CUDA-like platforms (MUSA, MACA): add the
new macros to all platform guard chains and register SDK paths in CMake.


---------

Co-authored-by: KarmaD7 <KarmaD7@users.noreply.github.com>
da9591685f
[TENT] Apply TE's changes of RDMA transport (#2102)
* [TENT] Refactor RDMA transport with unidirectional endpoint lifecycle

This commit aligns TENT RDMA transport with the source implementation,
focusing on endpoint lifecycle management, configuration consolidation,
and code organization improvements.

**Endpoint Lifecycle (Unidirectional)**
- Rename enum Status -> EndpointState to avoid ambiguity with mooncake::Status
- Define explicit state machine: EP_UNINIT -> EP_HANDSHAKING -> EP_READY -> EP_DESTROYING -> EP_DESTROYED
- Remove redundant active_/inactive_time_ members, use status_ for all state judgment
- Implement unidirectional lifecycle: endpoints never reset or reuse
- Add resetConnection() for marking failed endpoints for destruction
- Deprecate reset() to prevent accidental endpoint reuse

**Two-Phase QP Destruction**
- beginDestroy(): Mark endpoint as EP_DESTROYING, transition QPs to ERR state
- finishDestroy(): Wait for inflight WRs to drain, then destroy QPs
- Add destroy_start_time_ for timeout enforcement (30s default)
- Fix deconstructUnlocked() to maintain EP_DESTROYED state (no rollback to EP_UNINIT)

**Endpoint Store Cleanup**
- Unify remove() and removeRef() into single remove(RdmaEndPoint*) method
- Add terminal state checking in getOrInsert() - auto-remove and recreate
- Fix evictOne() to call beginDestroy() before moving to waiting_list
- Fix reclaim() to use finishDestroy() instead of getInflightSlices()
- Add waiting_list_len_ early return check in FIFO.reclaim()

**Configuration Management**
- Migrate PCIe Relaxed Ordering from environment variables to config
- Add backward compatibility mappings for legacy MC_* environment variables:
  - MC_NUM_CQ_PER_CTX, MC_NUM_COMP_CHANNELS_PER_CTX, MC_IB_PORT
  - MC_GID_INDEX, NCCL_IB_GID_INDEX, MC_MAX_CQE_PER_CTX
  - MC_MAX_EP_PER_CTX, MC_NUM_QP_PER_EP, MC_MAX_SGE, MC_MAX_WR
  - MC_MAX_INLINE, MC_PKEY_INDEX, MC_MTU, MC_IB_TC
  - MC_IB_PCI_RELAXED_ORDERING, MC_WORKERS_PER_CTX
  - MC_SLICE_SIZE, MC_RETRY_CNT, MC_DISABLE_GPU_DIRECT_RDMA
- Add RdmaTransport::config() public accessor for config-driven decisions

**Code Quality**
- Update state checks from CONNECTED -> EP_READY
- Simplify status checks by removing active_ dependency
- Improve logging for state transitions

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Reformat code

* Avoid use RdmaEndpoint::reset()

* Fix code issues

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4615104b8b
[Store] Report master metrics as per-second rates over time window (#2082)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: fatSheep <tzh2005t@163.com>
e0b1862a41
[Docs] fix ssd offload deployment doc (#2139)
Co-authored-by: liubaichuan <liubaichuan@infini-ai.com>
d9e8aee065
[TE] Update IntraNode NVLink transfer method cuMemcpy -> cuMemcpyAsync (#2012)
---------

Co-authored-by: 百麒 <yaozhong.lyz@alibaba-inc.com>
58ca374d78
ci: refactor TENT build and add test step (#2142)
- Remove CUDA-related flags and environment variables
- Add new test step for TENT unit tests

Signed-off-by: Yuxin Chen <grityxchen@gmail.com>
Co-authored-by: Yuxin Chen <grityxchen@gmail.com>
e73f785f2d
[CI/Build] Switch WITH_NVIDIA_PEERMEM to env variable (#2066)
* [CI/Build] Switch WITH_NVIDIA_PEERMEM to env variable

* [CI/Build] Switch WITH_NVIDIA_PEERMEM to runtime env variable

Instead of a cmake build-time option, check the WITH_NVIDIA_PEERMEM
environment variable at runtime in rdma_context.cpp and
rdma_transport.cpp to switch between ibv_reg_mr() and
ibv_reg_dmabuf_mr().

- Remove option(WITH_NVIDIA_PEERMEM) and add_compile_definitions() from
  common.cmake (no more compile-time flag)
- Update CMakeLists.txt to use GPU toolkit presence instead of cmake var
  for nvlink-allocator build condition
- Add withNvidiaPeermem() runtime helper reading WITH_NVIDIA_PEERMEM env
  var; default false (dmabuf path, no nvidia-peermem required)
- Replace #if !defined(WITH_NVIDIA_PEERMEM) && defined(USE_CUDA) guards
  with runtime if (!withNvidiaPeermem()) checks

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/05c94a1b-d4d6-4b44-be25-3b98d9b01f1b

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Add WITH_NVIDIA_PEERMEM to Environ; use Environ::Get() in rdma files

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/66e6cabb-c473-4a9d-9711-aebe468fcee2

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix linker error: link transfer_engine against mooncake_common for Environ::Get()

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/29d46e31-6dc1-4fee-be33-4a603537b827

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix linker error in Go CGO builds: add -lmooncake_common to build.sh scripts

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/ab73f098-d408-4ed0-95bb-77f9ce9f71ae

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Add USE_MACA to nvlink-allocator conditions to cover all GPU cases

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/251d2c35-d12a-43ed-9958-40526e0420d5

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix Go CGO linker path: add mooncake-common/src to library search paths

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/5a91e4b9-660d-463b-a8f2-d3fb0118fc34

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix Rust build.rs: add mooncake_common link and CUDA stubs search paths

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/e0d1e4fd-2cc5-4fe9-9268-5c205e3fc0f5

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix Rust build.rs: remove CUDA stubs from search_dirs to prevent runtime libcuda.so.1 dep

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/cd938ceb-c822-4439-b617-03f065015d4c

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix Rust build.rs: remove CUDA stubs from early rustc-link-search to prevent libcuda.so.1 runtime dep

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/055de577-7669-4039-a89a-d6066493e2d0

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

* Fix CI: create libcuda.so.1 symlink and set LD_LIBRARY_PATH for cargo test --lib

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/76131e72-de6f-485b-98ab-342b112f3968

Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>
5689013b52
[TENT] Proxy manager bugfixes (#2091)
* [TENT] Optimize ProxyManager staging performance

- Add comprehensive performance monitoring (ProxyManagerMetrics)
- Optimize staging buffer configuration (4MB chunks, 64 count)
- Implement intelligent retry mechanism for remote staging failures
- Fix event queue handling consistency in INFLIGHT_REMOTE state
- Add performance metrics: throughput, latency, retry counts, parallelism
- Improve error handling with configurable retry logic (default: 3 attempts)

Performance improvements:
- Better pipeline parallelism with 4-buffer circulation
- Reduced latency through async remote staging operations
- Enhanced reliability through smart retry mechanism
- Improved observability through detailed metrics collection

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* Reformat

* Fix build issue

* fix issues

* reformat

* trigger ci

* Revise prosy manager to fix potential bugs

* Fix GPU check to support staging

* Fix state machine

---------

Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
eaf724ab6e
[Store] Fix std::terminate crash on mooncake client shutdown (Issue#2115) (#2125)
* fix(store): join dummy_client_monitor_thread on shutdown to prevent std::terminate

The dummy_client_monitor_thread_ (std::thread) was started in
start_dummy_client_monitor() but never joined or detached. On
~RealClient(), the still-joinable std::thread triggers std::terminate(),
crashing standalone mooncake_client on every clean shutdown.

Add stop_dummy_client_monitor() following the same pattern as
stop_ipc_server(): set the running flag to false, then join. The call
is placed in tearDownAll_internal() before stop_http_server() and before
the dummy_client_mutex_ lock, avoiding the early-return skip and the
lock-then-join deadlock.


---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
a9e0aad723
[Doc] Update EFA transport doc: SGLang section + vllm-router + p5.48xlarge bench (#2154)
* [Doc] Update p6-b300 EFA throughput numbers post-#1944

Re-measured on a fresh main build between two p6-b300.48xlarge
instances. GPU-to-GPU peak 780 GB/s write (~97.5% of 800 Gbps line
rate, up from 752); CPU-to-CPU peak 283 GB/s write / 270 GB/s read
(up from 230/180). Drops the "predates the refactor" caveat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* [Doc] Update EFA transport doc: SGLang section + vllm-router + p5.48xlarge bench

- Rewrite "Usage with SGLang" to use sglang PR #25083 (MOONCAKE_PROTOCOL=efa
  flows natively) and mirror the vLLM section structure (Prefill / Decode /
  Router subsections). Drop unrelated GLOO_SOCKET_IFNAME and NVSHMEM bits.
  Note the cross-host router trap: PREFILL_HOST must be reachable from decode.
- Add Router subsection to "Usage with vLLM" using vllm-router with
  --kv-connector mooncake.
- Drop the SGLang Docker subsection (redundant).
- Add benchmark section "4. p5.48xlarge (H100, 32 EFA × 100 Gbps)" with
  GPU-to-GPU and CPU-to-CPU sweeps. Peak GPU 389 GB/s write / 382 GB/s read
  (~97% of 400 GB/s line rate). CPU plateaus at ~64 GB/s, bounded by
  DDR4-3200 on EPYC 7R13.
- Update Tuning Tips for the 32-NIC WR cap (8192 vs 4096 on 16-NIC hosts)
  and the read-batch difference between p5en and p5.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
076e3eaa5b
feat(store): add NoF segment metadata management (#2143)
Co-authored-by: Enigmo-x <guotaowei4@huawei.com>
32ccb9f7d6
[Store] correct GC interval default fallback in FromEnvironment (#2126)
* fix(file_storage): correct GC interval default fallback in FromEnvironment

The GetEnvOr fallback for client_buffer_gc_interval_seconds was
incorrectly using config.heartbeat_interval_seconds (10s) instead of
config.client_buffer_gc_interval_seconds (1s). This copy-paste bug
caused the GC thread to run every 10 seconds instead of every 1 second
when MOONCAKE_OFFLOAD_CLIENT_BUFFER_GC_INTERVAL_SECONDS is not set,
significantly delaying zombie buffer reclamation.

Fixes kvcache-ai/Mooncake#2119

* docs: update GC interval default value
1c3f1504a8
[TE] Fix pytorch precision problem when using IntraNode NVLINK (#2163)
* IntraNode NVLink async transfer

* Async intranode nvlink transfer

* Solve pytorch and Memcpy race condition by introducing sync event to make sure pytorch data prepared before transfer

---------

Co-authored-by: 百麒 <yaozhong.lyz@alibaba-inc.com>
47f88aa71c
[Store] Fix Ascend dummy reconnect shm replay (#2158)
Co-authored-by: youxiao <youxiao@huawei.com>
b4ccdc3082
Use sudo -E for make install in release workflow (#2169)
* Use sudo -E for make install in release workflow
7bf33267e2
[Doc] update WITH_NVIDIA_PEERMEM from cmake flag to runtime env var (PR #2066) (#2164)
Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/832212c9-6f77-4b98-bbe9-1a726e478400

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stmatengss <11641725+stmatengss@users.noreply.github.com>
5bf24ddf68
feat(rdma): add mlx5 direct verbs support for QP UDP sport override and LAG port balancing (#2175)
- Introduce`USE_MLX5DV`CMake option and link against`libmlx5`when enabled
- Add`MC_MLX5_QP_UDP_SPORTS`
environment variable to specify comma-separated UDP source ports for ECMP/LAG
path diversification
- Add`MC_MLX5_QP_LAG_PORT_BALANCE`
environment variable to enable automatic QP distribution across bonded LAG ports
- Update`RdmaContext`to query and expose the number of LAG ports via
`mlx5dv_query_device`
- Implement QP modification logic in`RdmaEndPoint::doSetupConnection`
to apply UDP source port and LAG port pinning
- Extend configuration parsing and logging to support the new mlx5-specific
options
- Document the new runtime options in the design documentation

Signed-off-by: staryxchen <staryxchen@tencent.com>
e0b0f01c42
[TENT] Enhanced QoS and Slice Spraying for TENT (#2048)
* Revise implementation

* Reformat

* fix fallback logic

* Add QoS APIs and docs

* Reformat

* Add QoS starvation prevention & bugfixes

* Reformat

* update docs

* remove tl_caller_id

* Fix worker distribution for multi-threaded submissions

When multiple threads submit slices simultaneously, they all start
distributing from worker 0, causing contention. Use thread_local
offset to distribute starting worker across threads, ensuring
each thread begins from a different worker.

Also rename submit_slices to next_worker_idx for clarity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0ba98bf0be
[TENT] Add codeowner to the TENT directory (#2183)
---------

Co-authored-by: Teng Ma <teng-ma@linux.alibaba.com>
8906bdef8f
[chore] Set default for WITH_NVIDIA_PEERMEM to true (#2192)
* [chore] Set default for WITH_NVIDIA_PEERMEM to true
d1312dce24
[Store] Add structured object store helper (#2140)
* [Python] Add structured object store helper

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
bd6d1125d2
[CI] Strip shared libraries to reduce NPU wheel size (#2202)
Co-authored-by: JieTang <tangjie66@huawei.com>
4cdfc65096
[Docs] Add Citation Paper (#2190)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
0b2058040c
[Doc]: Update vLLM LMCache guide for MP interface (#2209)
* [Doc]: Update vLLM LMCache guide for MP interface

* [Doc]: Update LMCache configuration for global segment and local buffer sizes
6b885426db
fix(file_storage): avoid INVALID_REPLICA error on empty offload heartbeat (#2151)
Skip BatchQuerySegmentSlices when no objects need offloading.
Fixes #2138.
db5e7f9c53
[Doc] Add missing aws-logo (#2211)
Co-authored-by: Ke Yang <yangke@approaching.ai>
ca7e5fbf95
python api register_memory support location param (#2191)
Co-authored-by: A-Liuhao <liuhao276@hisilicon.com>
1fd9699ec1
[Bugfix][Store] Fix snapshot failure when OpLog has never been written (#2144)
ResolveSnapshotSequenceId() only checks for ETCD_KEY_NOT_EXIST, but
EtcdOpLogStore::GetLatestSequenceId() translates that error code into
OPLOG_ENTRY_NOT_FOUND before returning. Add the missing error code to
the sentinel check so that an uninitialized OpLog is correctly treated
as sequence_id=0.

Signed-off-by: leonzzhu <leonzzhu@tencent.com>
cb032b24d6
[Build] Fix compile warnings across multiple components (#2193)
- mooncake-transfer-engine: add parentheses around && within ||, add
  static_cast for narrowing, mark unused function [[maybe_unused]]
- mooncake-store: fix member reorder warnings, add std::ignore for
  unused results, fix missing field initializers, mark unused variables
- mooncake-integration: fix sign-compare comparison, mark unused
  functions [[maybe_unused]]
- All fixes are semantic-preserving (no behavior changes)
a72b540cbc
Fix MACA nvlink allocator build by mapping CUmemAllocationHandleType (#2227)
Add the missing CUDA-like type alias in maca.h so nvlink_allocator.cpp
can compile when building with -DUSE_MACA=ON.

Co-authored-by: Cursor <cursoragent@cursor.com>
3dfee31522
[TransferEngine][MACA] Complete gpu_vendor/maca.h CUDA-like aliases for MACA build (#2230)
Add missing mappings in gpu_vendor/maca.h:
- CU_MEM_HANDLE_TYPE_POSIX_FILE_DESCRIPTOR -> mcMemHandleTypePosixFileDescriptor
- CU_POINTER_ATTRIBUTE_DEVICE_ORDINAL -> mcPointerAttributeDevice

These aliases complement #2227 and ensure full coverage of CUDA-like symbols
used in nvlink_allocator.cpp and related components when building with
-DUSE_MACA=ON.
54411aff58
[TENT] Add rule-based transport and device selection (#2079)
* [TENT] Add configuration-driven transport selector

Add TransportSelector for flexible, configuration-driven transport selection
policy while maintaining full backward compatibility.

Key features:
- Configuration-driven transport selection via JSON policy rules
- Support for segment_type filtering, device allocation, and transport priority
- Legacy mode option (use_legacy_transport_selection) for exact original behavior
- Default policies match original hardcoded behavior

Changes:
- Add TransportSelector class with SelectionContext, SelectionPolicy, SelectionResult
- Integrate TransportSelector into TransferEngineImpl
- Add legacy mode support to preserve original code path
- Restore TaskInfo fields (xport_priority, failover_count) for backward compatibility
- Add max_failover_attempts configuration option

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [TENT] Add comprehensive unit tests for TransportSelector

Add transport_selector_test.cpp with test coverage for:
- Default policies matching original behavior (File/Memory segments)
- Transport type name parsing
- Legacy mode enable/disable
- Transport availability based on capabilities
- Priority offset for fallback scenarios
- Device mask handling
- NVLINK same-machine constraint
- ROCm memory type support
- GPU-to-GPU, CPU-to-CPU, CPU-to-GPU, GPU-to-CPU transfers

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [TENT] Fix FakeTransport to use protected caps member

Fix compilation error by accessing Transport::caps (protected)
through helper methods instead of a separate public member.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix

* fix format issues

* Add priority-based rule

* Reformat

* Fix review comments

* Fix memory leak in endpoint_store_integration_test

When ibv_get_device_list returns a non-NULL list but num_devices == 0,
we need to call ibv_free_device_list before returning to avoid leaking
the allocated memory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Trigger CI

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d6b661d5a4
[TE] Add ProgressWorker skeleton (#2199)
* [TE] Add explicit progressBatch API

* [TE] Add progress worker skeleton
4794ed5aca
[Store] remove invalid kMaxSliceSize assertion in AllocateBatch (#2165)
* fix(store): remove invalid kMaxSliceSize assertion in AllocateBatch

The assert(sizes[i] <= kMaxSliceSize) in FileStorage::AllocateBatch was
an overly restrictive check copied from the CacheLib path. It caused
crashes when reading large offloaded objects (>4MB) from SSD, since
the local buffer allocator (AlignedClientBufferAllocator) can handle
arbitrary sizes. Put path already supports large objects by chunking,
but get path should not enforce the same limit on local temp buffers.

Fixes #2156

* Update mooncake-store/src/file_storage.cpp

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
eed58e89ed
feat(store): add SPDK NoF worker pool (#2172)
Signed-off-by: Enigmo <guotaowei4@huawei.com>
Co-authored-by: Enigmo-x <guotaowei4@huawei.com>
Co-authored-by: zwtao40 <1357420890@qq.com>
7d26a326e7
[Store] (CI run_tests_with_ssd failed / promotion-on-hit failed) eliminate race conditions (#2235)
* fix(transfer-engine): eliminate race condition in rePublishRpcMetaEntry

Remove the redundant storage_plugin_->remove() call before set().
All storage backends (HTTP PUT, Redis SET, Etcd put) have upsert
semantics, so remove-then-set creates a window where concurrent
get() returns empty / 404, causing transfer failures (-800).

Also change Json::UInt64 to Json::UInt for rpc_port to ensure
existing == desired comparison works correctly after JSON parse.

* fix: adjust eviction thread initial timing to prevent race in CI SSD tests

Start last_discard_time with an already-elapsed window so the first loop
iteration triggers DiscardExpiredProcessingReplicas immediately. Without
this, a task admitted shortly after thread startup can survive the first
reaper cycle and not be cleaned until ~2s later, causing promotion-on-hit
tests that sleep for 2s to flake.
0a0e952b1e
[TransferEngine][docs] document FI_EFA_USE_DEVICE_RDMA=0 for same-host EFA loopback (#2222)
* [TransferEngine] feat(efa): add MC_EFA_LOOPBACK_PREFER_EMULATED to recover same-host loopback throughput

After #2041 ([TE] fix(efa): request libfabric API 1.18 so device RDMA is the default on all EFA generations) the EFA provider unconditionally enables device RDMA on every supported EFA hardware. This is the right default for cross-host transfers -- it is what unlocks the 300+ GB/s benchmarks documented in this file -- but it regresses any Mooncake Store deployment that runs producer and consumer as separate processes on the same host (single-machine development, single-host benchmarks, co-located workers).

EFA NICs have no hardware loopback short-circuit: a "loopback" fi_write still drives a real DMA round-trip through the device (PCIe out, NIC SRD packet processing, PCIe back), so a same-host transfer pays full per-packet NIC overhead instead of taking the memcpy fast path that libfabric's emulated RDMA provider applies for same-host endpoints.

Measured on p5.48xlarge (1 NIC, 80 MB transfer, two Mooncake Store clients on the same host, put_from):

  FI_EFA_USE_DEVICE_RDMA=1 (default after #2041):  ~830 ms / call
  FI_EFA_USE_DEVICE_RDMA=0 (emulated):              ~390 ms / call

The 2.1x ratio is reproducible across runs; the emulated number is on par with what we measure for the same transfer cross-host with device RDMA on (~340 ms), once single-host memory-bandwidth contention is accounted for, confirming the slow path is NIC loopback rather than anything in the Mooncake Store layers above.

Add MC_EFA_LOOPBACK_PREFER_EMULATED as an explicit opt-in. When set to 1/true/yes/on, EfaContext::construct sets FI_EFA_USE_DEVICE_RDMA=0 before fi_getinfo so the EFA provider takes the emulated path. The env is opt-in, not auto-detect, because a single EfaTransport instance may serve a mix of loopback and cross-host peers, and FI_EFA_USE_DEVICE_RDMA is a provider-level flag resolved at fi_getinfo time -- flipping it disables device RDMA for every transfer in the process, including cross-host ones, which is exactly the wrong behavior for production fan-out. We use setenv(..., 0) so an explicit FI_EFA_USE_DEVICE_RDMA set by the user still wins over the opt-in.

Cross-host benchmarks are unaffected unless the env is also set on the cross-host process; the default behavior of this code path is unchanged.

A real fix (per-transfer same-host memcpy or cross-process zero-copy via process_vm_writev) is tracked in a follow-up issue. This change is the minimal mitigation users need today to avoid silently giving up half their single-host throughput.

* [TransferEngine] refactor(efa): address review feedback on MC_EFA_LOOPBACK_PREFER_EMULATED

Per maintainer review on #2222:

1. Register MC_EFA_LOOPBACK_PREFER_EMULATED in the Environ singleton
   (mooncake-common/{include/environ.h,src/environ.cpp}) instead of
   parsing it ad-hoc at the call site, so it shows up in the same
   inventory as every other tunable and goes through the existing
   GetBool() helper (which already handles 1/true/TRUE/on/yes).

2. Guard the setenv("FI_EFA_USE_DEVICE_RDMA", "0", 0) block with
   !std::getenv("FI_EFA_USE_DEVICE_RDMA"). This fixes two bugs:
     - We no longer log "-> FI_EFA_USE_DEVICE_RDMA=0" when the user
       has already set the env explicitly (setenv is a no-op there,
       so the old log line was misleading).
     - EfaContext::construct runs once per NIC (up to 32 times on
       p5.48xlarge); the getenv check causes the first NIC to set
       the env and subsequent NICs to skip the block entirely, so
       we log exactly once.

3. Drop the hand-rolled std::transform + ::tolower entirely (which
   was UB on signed char anyway -- flagged by Copilot and gemini)
   by delegating to Environ::GetBool. Removes <algorithm>, adds
   <cstdlib> for std::getenv/setenv.

Behavior is unchanged for the same-host case in the verification
table; this is purely structural cleanup.

* [TransferEngine] docs(efa): correct transfer size in verification table

The verification table in PR #2222 cited "80 MB transfer" as the
per-call payload, but the actual measurement was per ~1.2 GiB
(1218.8 MiB) ref blob (see ref_extractor log: blob_bytes=1218.8MiB
put=489.88ms). Update both the docs section and the in-code
comment to reflect the real transfer size. Latency numbers
(~830 ms / ~390 ms / ~340 ms) are unchanged -- they were always
measured on the 1.2 GiB blob.

Also collapse two single-statement multi-line getters/initializers
in mooncake-common to single-line form to match the existing
convention in environ.{h,cpp} (all other GetX() accessors are
single-line). No behavior change.

* [TransferEngine] docs(efa): drop MC_EFA_LOOPBACK_PREFER_EMULATED wrapper, document FI_EFA_USE_DEVICE_RDMA=0 directly

Per review feedback on #2222: the EFA user base is already familiar with
FI_EFA_USE_DEVICE_RDMA (it is documented by the EFA installer and
appears in every libfabric/EFA tuning guide), so wrapping it in a
Mooncake-namespaced alias does not pay for itself. The wrapper was a
literal one-to-one alias with no defaulting or transform.

Revert the Environ registration and the efa_context.cpp setenv block
(net code change for this PR becomes zero). Keep the diagnosis and the
verification table in efa_transport.md, but rewrite the recommendation
to point at FI_EFA_USE_DEVICE_RDMA=0 directly with the same
per-process / mixed-traffic caveat.

The long-term fix for same-host loopback (routing same-host
different-process transfers through process_vm_writev as a new
TransferStrategy::CROSS_PROCESS_MEMCPY, bypassing the NIC entirely)
remains tracked as #2223.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b10d5985ec
[CI] feat: pre-release ci workflow (#2212)
* Add pre-release CI workflow to validate release without PyPI

Introduce a tag-triggered pre-release workflow that mirrors the three
release build pipelines (CUDA 12, non-CUDA, CUDA 13), runs twine check,
and uploads wheels only as workflow artifacts.

Pre-release tags use semver suffixes (rc, alpha, beta, pre). Production
release workflows skip tags containing a hyphen so pre-release tags do
not publish to PyPI or create GitHub Releases.


---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Teng Ma <stmatengss@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Xun Sun <UNIDY2002@outlook.com>
c23d3081b1
[Doc] Clarify TENT failover poll behavior (#2208)
Document enable_auto_failover_on_poll so readers understand when status polling is observational only, and add both failover test commands so the verification steps from the PR follow-up are reproducible.
a18fad1f30
[Security] Fix Go vulnerabilities in libetcd_wrapper.so (#2250)
Update Go toolchain and dependencies to address CVEs:
- Go 1.25.9 → 1.25.10 (fixes CVE-2026-33814, CVE-2026-39836, CVE-2026-42499, CVE-2026-33811, CVE-2026-39820, CVE-2026-42501)
- golang.org/x/net v0.48.0 → v0.55.0 (fixes CVE-2026-39821, CVE-2026-33814)

This rebuilds libetcd_wrapper.so with patched Go stdlib and
golang.org/x/net to resolve downstream vulnerability scanner findings.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4569ce7d96
Build tent (#2089)
* Build with TENT

* Fix TENT failed start

* Revert

* Format

* Empty
f6b4adbc9a
[Store] Clarify cache stats semantics (#2248)
Signed-off-by: CAICAIIs <3360776475@qq.com>
079353e4b7
feat(store): route NoF replicas through put and get (#2247)
Co-authored-by: Enigmo-x <guotaowei4@huawei.com>
c391176477
[Doc] Split LMCache vLLM MP and non-MP guides (#2268)
* [Doc] Split LMCache vLLM MP and non-MP guides

* Simplified relative link to lmcache-integration.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Simplified relative link to lmcache-integration.md

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* [Doc] clarify LMCache Mooncake build requirements

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
c876cbe1c7
Fix engine.so runtime dependency (#2255)
Signed-off-by: Mohammad Miadh Angkad <176301910+mmangkad@users.noreply.github.com>
daa44a477a
[EFA] Add MC_EFA_CQ_THREADS env var to cap CQ poller threads (#2113)
* [EFA] Add MC_EFA_CQ_THREADS env var and reduce idle CPU spin

Two changes to EFA transport CQ polling:

1. Add MC_EFA_CQ_THREADS environment variable to cap the number of CQ
   polling threads. When running multiple EFA consumers (e.g. KV transfer
   + DeepEP all-to-all) in the same process, each creates threads per
   context. This allows limiting contention.

2. Replace std::this_thread::yield() with sleep_for(10us) in the idle
   path of workerThreadFunc. yield() on Linux compiles to sched_yield()
   which busy-spins at 100% CPU when there is no CQ work, wasting cores
   that could serve other EFA consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: move MC_EFA_CQ_THREADS to Environ singleton, revert yield change

Address reviewer feedback:
- Register MC_EFA_CQ_THREADS in Environ with GetInt (default 0 = unset)
- Use Environ::Get().GetEfaCqThreads() instead of raw std::getenv/stoull
- Revert yield() -> sleep_for() change (keep original yield behavior)
- Update comment to explain when/why the cap is useful

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add MC_EFA_CQ_THREADS documentation to EFA transport guide

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: change MC_EFA_CQ_THREADS default to 1 to reduce idle CPU spin

Benchmarks on p5.48xlarge show cap=1 reaches 99.93% of peak GPU-to-GPU
throughput (386.22 vs 386.48 GB/s) while freeing 31 cores from busy-spin.
Set MC_EFA_CQ_THREADS=0 to restore the legacy one-poller-per-context behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Ubuntu <ubuntu@ip-10-0-2-68.ec2.internal>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
766b83a6fd
[TransferEngine][ROCm] Add HIP dmabuf MR registration for AMD GPUs (fixes #751) (#2225)
* [TransferEngine][ROCm] Add HIP dmabuf MR registration for AMD GPUs

Fixes #751.

Adds a parallel `#elif defined(USE_HIP)` branch in
RdmaContext::registerMemoryRegionInternal that mirrors the existing CUDA
dmabuf path (added by #704) using ROCm's `hsa_amd_portable_export_dmabuf()`
instead of `cuMemGetHandleForAddressRange(...DMA_BUF_FD...)`. This lets
Mooncake register AMD GPU memory for RDMA without requiring an
nvidia-peermem-equivalent kernel module — the path UCX's ROCm backend
(uct/rocm/base/rocm_base.c) already uses successfully.

Same host-vs-device split as the CUDA branch: `hipPointerGetAttributes`
detects host memory and falls back to `ibv_reg_mr`; device/managed memory
goes through the dmabuf path. `hipMemGetAddressRange` is used to get the
true allocation base because `addr` may sit at an offset within a larger
hipMalloc block (caching allocators pack tensors).

CMake: added `hsa-runtime64` to the HIP link line in
mooncake-transfer-engine/src/CMakeLists.txt.

Validation:
- Standalone dmabuf probe verified PASS on:
  * AMD MI355X (gfx950) + Pensando ionic + ROCm 7.2.2
  * AMD MI300X (gfx942) + Broadcom Thor2 (bnxt_re) + ROCm 7.0.2
  Probe source + container recipe:
  https://github.com/andyluo7/dynamo/blob/amd-poc-consumer-polish/amd-mi355x-poc/advanced/debug-probes/dmabuf_register_probe.cpp
- Standalone compile check confirms all HIP/HSA/ibverbs symbols in the
  new branch resolve and link cleanly with hsa-runtime64 + libibverbs.

End-to-end SGLang+Mooncake disagg validation (T3) on MI355X+ionic will
follow in a comment once a full Mooncake build with submodules completes.

CC @misterwilliam @stmatengss @alogfans (active on #751)

Closes #751

---------

Signed-off-by: Andy Luo <anluo@amd.com>
Signed-off-by: Andy Luo <andy.luo@amd.com>
Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
579d42d354
[Store] L2->L1 promotion-on-hit: observability metrics + max_per_heartbeat knob (#2176)
* [Store] L2->L1 promotion-on-hit: Tier A observability + max_per_heartbeat knob

Adds Prometheus metrics for the promotion-on-hit funnel and exposes the
previously-hardcoded kMaxPerHeartbeat as a config knob.

Metrics (master_promotion_*)
----------------------------
Funnel:
  - promotion_in_flight (gauge): mirror of promotion_in_flight_
  - promotion_admitted_total: tasks past all gates, enqueued
  - promotion_completed_total: NotifyPromotionSuccess success
  - promotion_completed_bytes_total: bytes promoted (sum of source object_size)
  - promotion_failed_total: NotifyPromotionFailure accepted
  - promotion_expired_total: reaper Part 4 sweeps

Rejection (per gate):
  - promotion_rejected_frequency_total: below admission threshold
  - promotion_rejected_watermark_total: DRAM at or above high watermark
  - promotion_rejected_cap_total: promotion_in_flight at queue limit

Together: admitted = completed + failed + expired + in_flight. Wires a
working 'promotion feature health' Grafana panel: rate(admitted) vs
rate(completed) shows yield; rate(rejected_*) breaks down where work
is dropping; in_flight / promotion_queue_limit shows saturation.

promotion_max_per_heartbeat knob
--------------------------------
The previous compile-time constant kMaxPerHeartbeat = 1 (in
PromotionObjectHeartbeat) capped per-client throughput at ~6
promotions/min with the default 10s heartbeat, making
promotion_queue_limit = 50000 mostly theoretical.

Exposed as MasterServiceConfig::promotion_max_per_heartbeat, wired
through the flag parser (FLAGS_promotion_max_per_heartbeat) and
default_config.GetUInt32 path. Constructor clamps 0 -> 1 so a
mistyped config doesn't silently halt promotion delivery. Startup
log now includes max_per_heartbeat= alongside threshold= /
queue_limit=.

Default stays 1 (no behavior change on existing deployments).
Operators with small objects + RDMA-rich clusters can raise it; the
new MaxPerHeartbeatKnobControlsBatchSize test exercises 3.

Tests
-----
  - MetricsFunnelTracksSuccessfulPromotion: single full lifecycle
    bumps admitted/completed/completed_bytes correctly and brings
    in_flight back to baseline.
  - MetricsRejectionCountersIncrementOnGateMiss: each of frequency
    and cap counters increments when its branch fires.
  - MaxPerHeartbeatKnobControlsBatchSize: knob=3, 5 admitted tasks
    drain across 3+2+0 heartbeats.
  - MaxPerHeartbeatZeroClampsToOne: pathological config clamps to 1.

Suite: 32/32 pass (was 28; +4 new). No behavior change in existing
tests.

* [Store] L2->L1 promotion-on-hit: cover watermark gate in rejection test

MetricsRejectionCountersIncrementOnGateMiss claimed coverage of all
three rejection counters but only exercised frequency and cap. Add a
sub-case that forces the watermark gate by configuring
eviction_high_watermark_ratio = 0.0, asserting that
promotion_rejected_watermark_total increments.

* [Store] L2->L1 promotion-on-hit: cover RemoveAll/BatchRemove cleanup

upstream #2180 introduced EraseMetadataEntry as the centralized
metadata-erase helper and routed RemoveAll, BatchRemove, and
RemoveByRegex through it, so promotion_tasks cleanup on those paths
is already correct on main. The metric instrumentation
(dec_promotion_in_flight + inc_promotion_cancelled) is wired into
EraseMetadataEntry in this branch's earlier commit so every site
that erases metadata bumps the funnel counters consistently.

Add regression tests for the three paths so any future refactor
that reintroduces a metadata.erase without going through
EraseMetadataEntry will fail the suite:
- RemoveAllErasesPromotionTask
- BatchRemoveErasesPromotionTask (normal-completion branch)
- BatchRemoveStaleHandleErasesPromotionTask

* [Store] L2->L1 promotion-on-hit: bump reaper-test sleep margin to 3s

Three tests configure put_start_release_timeout_sec=1 and then sleep
2s waiting for the eviction-thread reaper to expire the promotion
task. The reaper schedule is `now - last_discard_time >
put_start_release_timeout_sec_` (strict greater-than), so a 1s
release with a 2s sleep leaves only ~1s margin between the reaper
firing and the assertion. CI runs observed intermittent failures
when scheduling jitter erased that margin.

Bump the sleep to 3s in the three affected tests so the margin is
~2s. Configuration values unchanged.

Affected:
- StalePromotionReaper
- RemoveDuringPromotion
- AllocStartRejectsReapedTask

Verified 5 consecutive clean runs of all three under -j1 build.
ecfa92d518
fix(metrics): show actual client-reported SSD capacity instead of infinite (#2278)
* fix(metrics): show actual client-reported SSD capacity instead of infinite

When no global file segment size limit is configured,
dfs_capacity_unlimited_ is set to true, causing the metrics
display to show 'infinite' for SSD Storage capacity regardless
of actual capacity reported by clients via ReportSsdCapacity.

Fix the display logic to only show 'infinite' when clients
have reported NO capacity (file_capacity == 0). If clients
have reported their actual SSD capacity via ReportSsdCapacity,
use that value instead. This respects per-client SSD capacity
limits even when no global limit is configured.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: align get_total_file_capacity and get_global_file_used_ratio with display logic

Address review feedback from gemini-code-assist: update programmatic
APIs to use the same (dfs_capacity_unlimited_ && file_capacity == 0)
condition as the display, so they don't unconditionally return
unlimited when clients have reported actual capacity.

* Apply suggestions from code review

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

---------

Co-authored-by: Autumn <Autumn@Autumns-MacBook-Air.local>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
bc17c9b60a
Fix P2PHANDSHAKE in dual-NIC container setups via MC_RDMA_BIND_ADDRESS (#2280)
* Initial plan

* Add MC_RDMA_BIND_ADDRESS support for dual-NIC P2PHANDSHAKE setups

In dual-NIC environments where TCP and RDMA use separate interfaces,
P2PHANDSHAKE mode previously required using a single IP for both
TCP handshake and RDMA NIC paths, causing conflicts.

This change adds MC_RDMA_BIND_ADDRESS env var support:
- When set, RDMA NIC paths use the RDMA-reachable IP
- TCP P2P routing continues using the local_server_name IP
- Segment descriptors carry rdma_server_name for consistent NIC
  path construction on both sides
- P2P metadata exchange caches RDMA->TCP address mapping so
  subsequent handshakes resolve to TCP-routable addresses

* Changes before error encountered

Agent-Logs-Url: https://github.com/kvcache-ai/Mooncake/sessions/fc2826eb-a0ae-450f-b1f4-4ab94269d97a

* Apply dual-NIC (MC_RDMA_BIND_ADDRESS) support to TENT transport and update Chinese docs

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
b6386a103d
[TE] IntraNode NVLink transport: update cuMemcpyAsync to BatchAsyc for CUDA version >= 12.8 (#2251)
* Update intraNode nvlink transport from MemcpuAsync to BatchAsync

* Code format update for intraNode nvlink

* Change addr to base_addr for register and unregister

---------

Co-authored-by: 百麒 <yaozhong.lyz@alibaba-inc.com>
3a1117bccc
[Build] fix(wheel): exclude libfabric/libefa from auditwheel bundle to avoid dual-libfabric EFA conflict (#2271)
* fix(wheel): exclude libfabric/libefa from auditwheel bundle

The wheel build runs `auditwheel repair`, which by default grafts every
non-excluded shared library the extension links against into the wheel's
`.libs/` directory and rewrites RPATH to prefer the bundled copy. The
exclude list already carves out the system RDMA/EFA stack (libibverbs,
libmlx5, libnuma, libcuda, ...) but missed libfabric and libefa.

As a result a pip-installed wheel ships its own
`mooncake_transfer_engine.libs/libfabric-<hash>.so.1` and `engine.so`
loads that instead of the system `/opt/amazon/efa/lib/libfabric.so.1`.
On AWS EFA hosts this puts two independent libfabric instances in one
process: Mooncake uses the bundled one, while aws-ofi-nccl (loaded by
NCCL) uses the system one. Each instance runs its own `ofi_hmem_init`
and opens EFA devices independently. When Mooncake initializes first it
claims the EFA device context; aws-ofi-nccl's later `fi_getinfo` then
returns "provider efa output empty list" and NCCL silently falls back to
the TCP provider (169.254.170.x), which hangs cross-node collectives
such as `all_gather_object`.

libfabric is the one library that MUST be shared with the system
aws-ofi-nccl plugin, so it has to come from the system just like
libibverbs/libmlx5 already do. Excluding it (and libefa) makes the wheel
load the same libfabric the rest of the EFA stack uses, eliminating the
dual-instance conflict.

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
94b9a6bc3e
[Store] fix: unify default cluster_namespace to match master's DEFAULT_CLUSTER_ID (#2244)
The client-side ResolveClusterNamespace() in etcd leader coordinator,
redis leader coordinator, and redis snapshot catalog store all used
'mooncake' as the fallback default, while the master-side --cluster_id
flag defaults to 'mooncake_cluster' (DEFAULT_CLUSTER_ID). This
inconsistency caused clients to look up a different etcd/redis key
than what the master registered, making them unable to discover the
master when both sides use defaults.

Fix by replacing the hardcoded 'mooncake' fallback with the
DEFAULT_CLUSTER_ID constant ('mooncake_cluster') so that client
and master use the same namespace out of the box.

Also update documentation to reflect the corrected default value.
6f22861dfd
[TE] fix: pass default port when parsing TENT RDMA bind a… (#2289)
* [TransferEngine] fix: pass default port when parsing TENT RDMA bind address

* [TransferEngine] fix: read TENT RDMA bind address from config
b4f47b4034
[Store] RemoveAll not deleting SSD offload files, enable storage_backend_->RemoveAll() in Client::RemoveAll (#2283)
* Enable storage_backend_->RemoveAll() in Client::RemoveAll

* complete

---------

Co-authored-by: ruanzhao <ruanzhao@kingsoft.com>
2e4a4fe1cc
[Doc] add vLLM scenario-based landing pages and archive legacy docs (#2262)
* [Doc] add vLLM scenario-based landing pages and archive legacy docs

* fix(docs): remove duplicate TENT section, fix num_workers indentation and benchmark version

- Remove duplicated "TENT Transport Selector" section in tent overview
- Fix num_workers indentation to reflect it's a top-level JSON key, not nested under kv_role
- Correct benchmark backend from V0 to V1 for vllm-benchmark-results-v1
1ca5410454
[Build] Disable debug symbols (-g) in default compilation flags (#2285)
Keep debug symbols enabled by default for local developer builds. CI test
workflows pass -DENABLE_DEBUG_SYMBOLS=OFF to reduce binary sizes during
testing. Release workflows are unchanged and retain debug symbols.
4c6a5367dd
[TE] Fix TCP connection pool SIGSEGV by deferring cleanup with asio::post (#2174)
* Use asio::post to defer cleanup

* Update lambda binding

* reformat
a2db8c05e2
[TENT] Add policy name binding to transport selector (#2295)
* [TENT] Add policy name binding to transport selector

Add ability to bind a request to a specific transport policy by name,
making the policy's "name" field in configuration actually useful.

Changes:
- Add optional `policy_name` field to Request struct (types.h)
- Add optional `policy_name` field to SelectionContext (transport_selector.h)
- Modify matchesPolicy() to prioritize exact policy name matching
  when context.policy_name is specified
- Pass policy_name from request to context in transfer_engine_impl.cpp

When a request specifies policy_name, the selector will only match
the policy with that exact name, ignoring other matching conditions
(segment_type, priority, memory_pattern, etc.).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Reformat

* Update mooncake-transfer-engine/tent/src/runtime/transport_selector.cpp

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Reformat

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
de19e8fbdf
[Store] Support SSD offload configuration in standalone store service (#2261)
and Python setup

Co-authored-by: 张翔云 <zhangxiangyun@cestc.cn>
8db42ca973
[TransferEngine] Make TCP transport slice size configurable via MC_TCP_SLICE_SIZE (#2308)
The TCP transport previously hardcoded a 64KB (65536 bytes) slice size
for splitting large transfers into socket read/write operations. This
commit makes it configurable via the MC_TCP_SLICE_SIZE environment
variable, consistent with the RDMA transport's MC_SLICE_SIZE naming.

Usage: export MC_TCP_SLICE_SIZE=1048576  # 1MB slices
Default: 65536 (64KB, unchanged)
c6152c9ca3
[Build] Allow building tebench without USE_TENT (#2322)
Separate the tebench benchmark from the TENT build so it can be compiled
with only the classic Transfer Engine backend.

- Gate the benchmark subdirectory solely on BUILD_BENCHMARK instead of
  also requiring USE_TENT.
- Drop tent_backend.cpp from the sources and only link tent_link_group /
  define USE_TENT when USE_TENT is enabled; otherwise expose just the
  tent/include header path for the header-only helpers used by the
  classic backend.
- Guard the TENT backend include and runner in main.cpp, returning an
  error when a TENT-only backend is requested in a non-TENT build.
5dbc1b7088
fix(ci): move sccache --show-stats to after build steps (#2303)
* fix(ci): move sccache --show-stats to after build steps

The "Run sccache stat for check" step was running before any build
step, so it always reported 0 cache hits and 0 cache misses. Move it
to after the build so it shows actual sccache statistics.

* fix(ci): move sccache stat after nvlink_allocator build in first job

In the first `build` job, the sccache stats step was placed after `Build
project` but before `Build nvlink_allocator.so`, missing the nvlink_allocator
compilation from the cache statistics. Move it after `Build nvlink_allocator.so`
to match the ordering in the `build-flags` job and capture all compilation steps.
f23575169d
[Docs][1/N] Refactor Readme: update readme top link and badges (#2304)
* Update readme top icons

* remove duplicate links

* update pypi icon

* fix format error

* fix a link error

---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
268622fa70
[TE] fix(efa): short-circuit same-process GPU loopback to avoid libfabric SHM segfault (#2298)
* fix: route EFA same-process loopback transfers through local copy

The EFA provider's SHM intra-node path performs a host memcpy into
FI_HMEM_CUDA device buffers and segfaults on the first same-host
transfer (loopback self-transfer), e.g. checkpoint-engine P2P weight
update on a single TP=8 node. See ofiwg/libfabric#12328.

Detect same-process self-loopback in EfaContext::submitPostSend (peer
NIC path equals our own nicPath(), whose server_name embeds the
per-process RPC port, so the match guarantees the peer is this very
process on this device) and satisfy the copy locally with a GPU-aware
cudaMemcpy (cudaMemcpyDefault), bypassing EFA entirely. Same-host
cross-process peers carry a different port, never match, and still go
through EFA.

The copy direction honors the slice opcode, mirroring fi_read/fi_write:
WRITE copies source_addr -> dest_addr, READ copies dest_addr ->
source_addr (the two are distinct local buffers, so it is not a
symmetric self-copy).

This mirrors how the RDMA transport already treats loopback as a
special case (rdma_endpoint.cpp self-connected QP); RDMA relies on NIC
hardware loopback and is unaffected by the libfabric SHM bug.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test: add EFA GPU loopback test for FI_HMEM_CUDA same-host transfers

Add efa_gpu_loopback_test.cpp, the CUDA-device-memory counterpart of
efa_transport_test.cpp (which only covers host/numa loopback). It
reproduces the EFA SHM intra-node segfault on FI_HMEM_CUDA buffers
(ofiwg/libfabric#12328) and validates EfaContext::tryLoopbackCopy:

  * GpuLoopbackWrite       — same-host GPU WRITE must not crash.
  * GpuLoopbackWriteThenRead — WRITE then READ with byte-accurate
    verification, exercising both copy directions.
  * GpuLoopbackMultiWrite  — batched GPU writes through the per-slice
    loopback short-circuit.

Self-skips when no EFA device or no CUDA GPU is present. Registered
under `USE_EFA AND USE_CUDA`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* efa: log fabric name and document same-host GPU loopback segfault

Add fabric_attr->name to the EFA device init log so the active fabric
(efa-rdm vs efa-direct) is visible at runtime — both share the same
provider name, FI_EP_RDM type, and <device>-rdm domain name, so the
fabric name is the only field that distinguishes them.

Revise the "Single-host loopback" doc section:
- Correct the same-host fast-path attribution: the memcpy fast path is
  supplied by the SHM provider (FI_EFA_ENABLE_SHM_TRANSFER, default on),
  not by FI_EFA_USE_DEVICE_RDMA. Confirmed at runtime: a default
  (device-RDMA-enabled) config still reports "Opened fabric: shm".
- Add a warning that the default SHM path host-memcpy's into FI_HMEM_CUDA
  destinations and segfaults on GPU buffers (ofiwg/libfabric#12328);
  document the same-process tryLoopbackCopy short-circuit and the
  FI_EFA_ENABLE_SHM_TRANSFER=0 workaround for cross-process GPU peers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
bab9659764
[Docs] Add build guidance for npu platform (#2325)
Co-authored-by: ZhaoBaiwei <zhaobaiwei@huawei.com>
a1495cce80
[Bugfix][Store] Fix HA snapshot restore rejecting newer metadata formats (#2257)
* [Bugfix][Store] Fix HA snapshot restore rejecting newer metadata formats

The master snapshot writer (MasterService::MetadataSerializer) packs each
object's metadata as `9 + replica_count` fields: a data_type after
replicas_count and a trailing hard_pinned flag. The HA standby restore
reader in catalog_backed_snapshot_provider.cpp only accepted
`7 + replica_count` / `8 + replica_count` and assumed replicas began
immediately after replicas_count, so it never skipped data_type and
rejected current-format entries with:

  Snapshot metadata entry replica count mismatch, replicas=1, total_fields=11
  Failed to deserialize snapshot metadata payload ... DESERIALIZE_FAIL
  Failed to load snapshot baseline, falling back to OpLog-only bootstrap

Port the master-side format detector (v1/v2/v3) into the standby reader so
it skips data_type when present and tolerates the trailing hard_pinned,
matching every shape the writer emits.

Add round-trip tests covering the data_type-only (8+rc), hard_pinned-only
(8+rc), and current (9+rc) formats; the 9+rc case is a regression test for
the live failure.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: fatSheep <tzh2005t@163.com>
5889787dbe
Optimize ascend_direct async query and route failure propagation. (#2323)
Cache engine and target in QueryBatch to avoid per-poll metadata lookups,
propagate route failures only to batches already in pending_batches with
a single disconnect, and treat NOT_CONNECTED as success on auto_connect
disconnect.

Co-authored-by: Developer user <youxiao@huawei.com>
5b9a395cc5
[TransferEngine] Fix resource leaks in error paths (#2332)
1. multi_transport.cpp: Delete Transport object when install() fails.
   The raw pointer was leaked on the error return path.

2. transfer_engine_c.cpp: Add null check after malloc and early return
   when size is 0 in getNotifsFromEngine(). Prevents null dereference
   in memset when malloc fails, and avoids implementation-defined
   behavior of malloc(0).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
7de0cca2a1
[Common] Harden Environ parsing, fix opendir leak, support .yml config (#2316)
* [Common] Harden Environ parsing, fix opendir leak, support .yml config

Fixes #2315

1. Fix: Replace opendir() with stat()+S_ISDIR() in config.cpp to fix
   DIR* handle leak.

2. Robustness: Replace atoi() with strtol() in Environ::GetInt with
   endptr/errno validation. Invalid values now fall back to default
   with a warning instead of silently returning 0.

3. Robustness: Add leading '-' rejection in Environ::GetSizeT to
   prevent strtoull negative wrapping (e.g. MC_SLICE_SIZE=-1 yielding
   ULLONG_MAX).

4. Feature: Support .yml extension in DefaultConfig::Load().

5. Tests: Add 26 unit tests for Environ::GetInt/GetSizeT/GetBool/
   GetString covering valid, invalid, missing, overflow, negative,
   and trailing garbage inputs. Tests call the real production code.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Common] Use strtoll instead of strtoull in GetSizeT for robust negative handling

Address Gemini review: val[0]=='-' check missed leading-whitespace
cases like " -1". Using strtoll catches negatives regardless of
whitespace, and also guards against 32-bit SIZE_MAX truncation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Common] Address review: restore comments, use std::filesystem

- Restore helper method comments stripped when moving to public
- Switch from stat()+S_ISDIR() to std::filesystem::is_directory()
  per reviewer suggestion (C++20 project, already used elsewhere)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Common] Fix clang-format violation in environ.cpp

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Common] Address Copilot review: non-throwing is_directory, fix stray PUBLIC

- Use std::error_code overload of std::filesystem::is_directory to
  avoid throwing on permission errors (EACCES). Matches original
  opendir() non-throwing behavior.
- Remove pre-existing stray PUBLIC keyword in tests/CMakeLists.txt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
40935abd44
[Wheel] Fix _parse_segment_size to support KB/MB/TB suffixes and fix handle_put None crash (#2321)
* [Wheel] Fix _parse_segment_size to support KB/MB/TB suffixes and fix handle_put None crash

Fixes #2320

1. Fix: _parse_segment_size now supports KB/MB/GB/TB/B/K/M/G/T
   suffixes with float values (e.g. "1.5gb", "512mb"), matching C++
   try_string_to_byte_size. Bare float strings (e.g. "1.5", "1e9")
   are also handled via int(float(s)) fallback.

2. Fix: handle_put guards against value=None before calling .encode(),
   preventing AttributeError crash on malformed PUT requests.

3. Tests: 13 new test cases for _parse_segment_size covering all
   suffixes, floats, empty strings, missing numbers, and invalid input.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
d36a72f0e1
[TE] add device API support (#2333)
* feat(transfer-engine): extract Device API and IBGDA transport layer

Move IBGDA files from mooncake-ep to mooncake-transfer-engine (git mv):
- 6 headers: mooncake_ibgda/ → transport/device/ibgda/
- 1 source: mlx5gda.cpp → transport/device/mlx5gda.cpp

Add new Device API layer under transport/device/:
- device_transport.h: P2pTransport + RdmaTransport interfaces
- device_ops.cuh: DeviceOps function pointer table (bottom IR)
- comm_device.cuh, p2p_device.cuh, ibgda_device.cuh: device contexts
- cuda_ops.cuh, musa_ops.cuh: platform DeviceOps implementations
- ibgda_device_transport.cpp: RdmaTransport IBGDA implementation
- p2p_device_transport.cpp: P2pTransport NVLink/MTLink implementation

Modify transfer-engine:
- transfer_engine.h/impl: add getOrCreateP2pTransport/RdmaTransport
- gpu_vendor/musa.h: add MUSA API aliases
- Build: link mlx5, add device/ subdirectory

Minimal EP changes (include paths + build system only):
- Update include paths to new transport/device/ibgda/ location
- Remove mlx5gda.cpp from EP sources
- Forward EP_USE_MUSA env var in BuildEpExt.cmake

Root CMakeLists.txt: guard CUDAToolkit with USE_CUDA

* feat(example): add device_transport_example for P2P Device API

Two-rank example demonstrating the full Device API lifecycle:
- Host side: P2pTransport for IPC handle exchange and peer mapping
- Device side: CommCtx + mc_route_put + mc_signal for GPU-initiated
  P2P data transfer and notification

Uses file-based IPC handle exchange (no external dependencies).
Requires 2 GPUs with P2P access (NVLink or PCIe).

* fix(example): handle missing TORCH_CUDA_ARCH_LIST

* fix(example): convert torch arch format to CMake CUDA format

* fix(example): construct CommCtx manually on host side

* fix(example): add using namespace mooncake::device in kernels

* fix(example): pass CommCtx by value, add barrier before cleanup

- Pass CommCtx by value to kernel (CUDA copies to param space) instead of
  dereferencing a device pointer on host (caused segfault).
- Add file-based barrier so rank 0 waits for rank 1 to finish before
  freeing its GDR buffer (IPC handle only valid while allocation exists).
- Change default metadata_server to P2PHANDSHAKE (no etcd dependency).

* fix: address review feedback — fence ordering, error checks, QP leak

- musa_ops.cuh: fix fence ordering for acquire/release semantics
  (fence after load for acquire, before store/atomic for release)
- ibgda_device_transport.cpp: check cudaMalloc return values,
  add num_qps >= num_ranks guard, destroy QP on rst2init failure
- p2p_device_transport.cpp: add device_count > 0 guard
- device_transport_example.cu: validate kDataBytes % 16 == 0
  and kDataBytes <= kSignalWordOffset

* style: apply clang-format to Device API files

* fix: guard device transport code with USE_CUDA/USE_MUSA macros

The device transport accessors (getOrCreateP2pTransport,
getOrCreateRdmaTransport) and their member variables were not guarded
by USE_CUDA/USE_MUSA preprocessor macros. When building with
USE_CUDA=OFF (the default), the device transport source files aren't
compiled but the headers and implementations still reference them,
causing linker errors in CI build-flags and build jobs.

* fix: set CMAKE_CUDA_STANDARD 20 and make ibgda PUBLIC

- common.cmake: add CMAKE_CUDA_STANDARD 20 so nvcc compiles host code
  in C++20 mode, matching CMAKE_CXX_STANDARD.  Fixes "starts_with is
  not a member of std::string" when compiling .cu files that indirectly
  include common.h.
- transport/CMakeLists.txt: change ibgda from PRIVATE to PUBLIC so
  mlx5gda_* symbols are visible to downstream consumers (Go p2p store
  via transfer_engine).  Fixes undefined reference errors for
  mlx5gda_destroy_qp, mlx5dv_devx_umem_reg, etc.

* fix: compile mlx5gda into device_transport and link mlx5 for Go consumers

The previous attempt (PUBLIC ibgda) did not work because the Go p2p-store
and mooncake-store binaries link libtransfer_engine.a via hand-written cgo
ldflags, which bypass CMake's target_link_libraries propagation entirely.

- transport/device: compile mlx5gda.cpp directly into the device_transport
  OBJECT library (like every other transport module) instead of a separate
  ibgda STATIC lib, so mlx5gda_* symbols flow into libtransfer_engine.a and
  are visible to all consumers regardless of how they link.
- transport: link libmlx5 (PUBLIC) since ibgda_device_transport.cpp /
  mlx5gda.cpp call mlx5dv_devx_* / mlx5dv_init_obj directly.
- p2p-store/build.sh, mooncake-store/go/build.sh, ci.yml: add -lmlx5 to the
  hand-written cgo ldflags so the DevX symbols resolve.
- example: set CUDA_STANDARD 20 on device_transport_example so nvcc compiles
  common.h (std::string::starts_with) in C++20 mode.

* fix: CUDA_EXTENSIONS OFF for example, add -lm for Go consumers

Follow-up to compiling mlx5gda.cpp into device_transport:

- example: nvcc has no gnu++20 dialect, so CUDA_STANDARD 20 with the default
  CUDA_EXTENSIONS=ON fails at CMake generate ("does not know the compile
  flags").  Set CUDA_EXTENSIONS OFF to request plain -std=c++20.
- p2p-store/build.sh, mooncake-store/go/build.sh: mlx5gda.cpp uses log2/ceil
  (<cmath>); now that its object lives in libtransfer_engine.a, the hand-
  written cgo ldflags need -lm to resolve log2@GLIBC_2.29.  (ci.yml already
  had -lm.)

* fix(ci): gate Device API GPU example off by default, link mlx5 for Rust

The Docker build failed at CMake generate because device_transport_example
needs the CUDA20 dialect (transfer_engine.h -> common.h uses C++20
std::string::starts_with), which the older CMake in the CI image cannot
enable. CUDA_EXTENSIONS OFF did not help since the limitation is the CMake
version, not the dialect flavor. Gate this manual, 2-GPU example behind a
new BUILD_DEVICE_TRANSPORT_EXAMPLE option (default OFF) so the default build
no longer requires CUDA20.

Also link mlx5 in the mooncake-store Rust build script: the IBGDA device
transport (mlx5 DevX) is now compiled into transfer_engine, so the Rust
test link step needs -lmlx5 to resolve mlx5dv_devx_* symbols.
938d0c7069
[PG] Fix null-deref on MNNVL disconnect and activeRanks leak (#2347)
* [PG] Fix null-deref on MNNVL disconnect and activeRanks leak

- Guard warmup_recv_region_ dereference in pollPeer disconnect
  path: on MNNVL/fabric clusters (skip_warmup_==true), the region
  is nullptr and peer disconnect from CONNECTED state crashes.
- Free meta_->activeRanks in shutdown() to match the allocation
  at init (new[] for CPU, cudaHostAlloc for GPU).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Null activeRanksDevice after cudaFreeHost in shutdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
47b267a5a1
[PG] Fix data race: make running_ atomic (#2352)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
27aaae1e94
[PG] Build the MUSA PG extension through torchada (#2353)
* [PG] Build the MUSA PG extension through torchada1

Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>

* Address review comments

Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>

---------

Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
5893083ab1
[TENT] Validate minimum request size before XferDataDesc cast (#2351)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
fc7b8eb958
[TE] Fix data race, double-close, uninit, and off-by-one in RDMA transport (#2346)
* [TE] Fix data race, double-close, uninit, off-by-one in RDMA transport

- Remove 'static' from access_rights in registerLocalMemoryInternal
  to eliminate data race under concurrent registerLocalMemoryBatch.
- Add event_fd_ = -1 after close(event_fd_) on 3 error paths in
  RdmaContext::construct() to prevent double-close in destructor.
- Value-initialize comp_channel_ array to zero so partial-failure
  cleanup in deconstruct() sees nullptr instead of garbage pointers.
- Fix off-by-one: change > to >= in doSetupConnection bounds check
  to prevent OOB access when qp_index equals qp_list_.size().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [TE] Value-initialize wr_depth_list_ to fix sibling uninit bug

Address review: wr_depth_list_ has the same uninitialized-array
bug as comp_channel_. Partial QP creation failure leaves garbage
values that corrupt the CQ outstanding counter in destructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Init wr_depth_list_ to nullptr, guard deconstructLocked()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
7b8f386144
[TE] Harden config parsing: remove exit() and guard stoi (#2344)
- Remove exit(EXIT_FAILURE) on invalid MC_MTU so the process
  continues with the default IBV_MTU_4096 instead of crashing.
- Wrap MC_HANDSHAKE_LISTEN_BACKLOG std::stoi in try-catch to
  match MC_PKEY_INDEX / MC_IB_TC pattern and prevent crash on
  non-numeric input.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
c142b40590
[TE] Fix error-path safety: freeaddrinfo leak, null deref, OOB read (#2343)
- Fix freeaddrinfo leak on ERR_MALFORMED_JSON early returns in
  sendNotify, sendProbe, send, and exchangeMetadata.
- Add null check for getSegmentDescByID in sendNotifyByID to
  prevent null pointer dereference on invalid segment ID.
- Guard readString against zero-length network input to prevent
  OOB access on empty buffer.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
5771febf40
[TE] Fix lock leak, missing transport_, and empty entries UB (#2349)
* [TE] Fix lock leak, missing transport_, and empty entries UB

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix pre-existing clang-format violations in dump.cpp

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
9153043b86
[TE] Harden TCP transport: validate remote addresses, fix idle cleanup, add TCP_NODELAY (#2314)
* [TE] Harden TCP transport: validate remote addresses, fix idle cleanup, add TCP_NODELAY

Fixes #2313

1. Security: ServerSession now validates remote-supplied memory addresses
   against registered local buffers before use, preventing arbitrary
   memory read/write from malicious peers.

2. Performance: Set TCP_NODELAY on all server-accept and client-connect
   paths to eliminate Nagle-induced latency on small control messages.

3. Correctness: Call io_context.restart() after exception in worker loop
   to prevent busy-spin when io_context enters stopped state.

4. Correctness: Fix cleanupIdleConnections to scan the full deque instead
   of only the back, so idle connections anywhere in the pool get cleaned.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [TE] Fix clang-format violations in TCP transport

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
f7380ba057
[Store] master log journal (#2297)
* feat(master): merge master logs into a single journal directory via glog

Add a MasterLogJournal module that configures glog so all master
diagnostic records (LOG()/VLOG()) are merged into one chronologically
ordered file inside a dedicated journal directory, instead of glog's
default per-severity files scattered next to the binary.

The design reuses glog rather than reimplementing a logging engine:
- keeps only glog's lowest-severity sink (which already receives every
  record) and disables the higher-severity sinks to merge into one file
- reuses size-based rotation (FLAGS_max_log_size), time-based retention
  (google::EnableLogCleaner), the latest-file symlink, periodic flushing
  (FLAGS_logbufsecs) and stderr mirroring (FLAGS_stderrthreshold)

Wire it into mooncake_master via new flags (--enable_log_journal,
--log_journal_dir, --log_journal_merge, --log_journal_max_file_size_mb,
--log_journal_retention_days, --log_journal_also_log_to_stderr), add a
unit test, and document the flags in the deployment guide.

Co-authored-by: Teng Ma <stmatengss@users.noreply.github.com>
f344d4e2ce
[Store] Remove HAMetricManager::Init() (#2287)
* [Store] Move HAMetricManager::Init() from scattered locations to main()

HAMetricManager::Init() was called from two places — MasterAdminServer::Start()
and HotStandbyService constructor — neither of which owns the HA metrics
lifecycle. Both HA and standalone code paths depend on HAMetricManager via the
/metrics endpoint's serialization, so initialize it once in main() before
either path diverges.

- master.cpp: add Init() before the enable_ha branch
- rpc_service.cpp: remove Init() from MasterAdminServer::Start()
- hot_standby_service.cpp: remove Init() from constructor

* Remove HAMetricManager::Init() — C++11 magic statics already guarantee thread-safe lazy initialization
bd3dcd6fd5
[Store] Clean up tenant-aware master service APIs (#2337)
* [Store] Clean up tenant-aware master service APIs

* [Store] Remove legacy tenant default helpers
0cd30bc602
[Store] Introduce buffer pool for zero copy interfaces (#2095)
* [Store] Add native registered buffer pool

Add a native Python registered buffer pool for reusable zero-copy scratch buffers, with lease lifetime checks and docs/tests for the public API.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
3c1801bcd5
[Common][Store][TE] Fix undefined behavior passing signed char to ::tolower (#2367)
std::transform(..., ::tolower) passes each std::string element directly
to ::tolower. On platforms where char is signed, any byte > 0x7F is
sign-extended to a negative int, which is undefined behavior for
std::tolower/::tolower (the argument must be representable as unsigned
char or equal EOF). In practice this can index out of bounds in the
ctype table and crash, or yield wrong results, when an env var or config
string contains non-ASCII bytes.

Wrap the call in a lambda that casts to unsigned char before calling
std::tolower, the standard-conforming idiom, and add the corresponding
<cctype>/<algorithm> includes where they were only available
transitively.

Fixes the same UB in all four occurrences:
- Environ::GetBool                (mooncake-common/src/environ.cpp)
- TransferSubmitter ctor          (mooncake-store/src/transfer_task.cpp)
- ConfigHelper::parseBool         (tent/src/common/config.cpp)
- TransportSelector::loadPolicies (tent/src/runtime/transport_selector.cpp)

Co-authored-by: Chelseatr <keldnielsen686@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
3adc636216
[TENT] Add local admission queue prototype (#2341)
* Introduce TENT admission queue scaffold

* Implement TENT queue admission accounting

* Add TENT queue dispatch lifecycle

* retire batches by task range

* validate queue limits at construction
5e5052fef8
[TE] Improving the RDMA transport failure handling (#2155)
* [TE] Simplify failure handling with rail pause mechanism

This commit refactors the RDMA transport failure handling to use a
simplified rail pause mechanism instead of per-endpoint active state
tracking and global RNIC error counting.

Key changes:
- Add RailState management: tracks error count per peer_nic_path
- markRailFailed(): increments error count, pauses rail after threshold
- isRailAvailable(): checks rail availability with auto-recovery
- submitPostSend(): proactively switch to alternative device if rail paused
- Remove endpoint->active() state tracking
- Remove success_nr_polls/failed_nr_polls global counters
- Simplify WC error handling: always redispatch on failure

The new mechanism provides transient link avoidance without permanent
blacklisting, automatically recovering after the pause period.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Reformat

* add a test script

* fix format

* Remove test code

* retrigger

* reformat

* Recover IBV_WC_WR_FLUSH_ERR handle logic

* remove reset failure counter periodically

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c7b7526a5a
[TransferEngine] Fix: TCP transport implicitly creates CUDA context on GPU0 (#2307)
* [TransferEngine] Fix: TCP transport implicitly creates CUDA context on GPU0

When TCP transport transfers GPU memory, asio worker threads (which never
call cudaSetDevice) execute cudaMemcpy, causing the CUDA Runtime to
implicitly initialize a primary context on the default device (GPU0).
This wastes ~520 MiB of GPU0 memory per worker process.

Fix: replace isCudaMemory() with getCudaDevice() which returns the device
ordinal, and call cudaSetDevice(cuda_device) before every cudaMemcpy so
the Runtime uses the correct GPU instead of defaulting to GPU0.

Root cause verified on H200 cluster: TCP transfer +521 MiB on GPU0 before
fix, +0 MiB after fix.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* [TransferEngine] Refactor: keep isCudaMemory() and add getCudaDeviceId()

Per review feedback on PR #2307: instead of replacing isCudaMemory() with
getCudaDevice(), keep isCudaMemory() as a lightweight helper and introduce
a separate getCudaDeviceId() function that returns the device ordinal.
Rename all call sites from getCudaDevice() to getCudaDeviceId() accordingly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* ci: retrigger CI

* ci: retrigger CI

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
0db01ecef0
Add modular KVCache storage benchmark (v1) (#2368)
* Add modular KVCache storage benchmark (v1)

- Refactored benchmark into modular architecture
- Separated concerns: benchmark logic, layout implementations, storage backends
- Added KVLayout interface for architecture extensibility (currently MLA)
- Made page_size_tokens configurable via CLI
- Support for multiple models (glm5, kimi-k2.6) and fsync modes
- Clean separation: benchmark.py depends only on storage, layout contains architecture details

* Apply suggestions from code review

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Update benchmark and test okay

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
d00afc7fb4
[Store] Introduce cached batch query result (#1834)
* [Store] preserve cached-query semantics across shm reads

Keep query-result caches request-scoped while carrying lease/error semantics through DummyClient shm reads, and trim redundant cache copies in unified and Engram read paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] simplify cached-query reuse path

Keep cached-query reuse focused on preserving QueryResult and collapse duplicated batch-read plumbing so unified and Engram paths share the same minimal execution flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] trim cached-query plumbing

Keep cached-query reuse focused on request-scoped QueryResult flow, drop the extra metadata-prefix helper surface, preserve cached failures across SHM reads, and narrow reconstruction plans to carry only the query results they actually execute.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] tighten cached-query scope

Restore RealClient's private ranged-read metadata flow and keep the cached-query change focused on request-scoped QueryResult reuse instead of broader batch-read refactors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] restore ranged-read layout comments

Move the ranged-read helpers back to their prior locations and restore the LOCAL_DISK/DISK execution comments so the cached-query diff stays readable and close to the original structure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] restore header declaration layout

Move the ranged-read declarations back next to the surrounding internal read helpers so the cached-query change stays scoped and easier to review.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] reuse cached queries across reconstruction reads

Reuse query results for reconstruction metadata and execution so unified parallel reads and Engram avoid duplicate BatchQuery work, and centralize cached-query conversion helpers in the shared client layer.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] reduce test_44 same-key contention

Use unique keys in the unified parallelism concurrency matrix test so 8-core runners still exercise concurrent relation coverage without piling repeated writes onto the same objects.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] harden reconstruction query reuse

Avoid serializing reconstruction metadata reads on shared scratch state and only reuse cached query results when they are still valid, so fallback metadata loads do not keep stale or failed routing alive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] add formula full reconstruction into path

Use a formula plan for regular full reconstruction reads to reduce cold planning overhead while preserving generic fallback behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] address formula reconstruction review feedback

Tighten the formula reconstruction path and related query reuse changes by validating shard metadata, avoiding hot-path casts, preserving failed cached query results, and removing legacy comparison hooks from tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] fix rebased full reconstruction build

Remove a duplicate writable buffer region declaration left by conflict resolution so the Python store extension builds after rebasing onto main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
076636ccf0
[TE] Guard stoull in EFA getMaxPteEntries against invalid input (#2363)
* [TE] Guard stoull in EFA getMaxPteEntries against invalid input

std::stoull in a static initializer throws on non-numeric input,
causing std::terminate. Add try-catch to fall back to default.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Reject negative MC_EFA_MAX_PTE_ENTRIES values

stoull accepts "-1" and wraps to ULLONG_MAX, passing the val > 0
check. Add minus-sign guard to treat negative input as invalid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix clang-format violation in getMaxPteEntries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Replace negative-sign check with range validation

Per reviewer feedback: use upper-bound check instead of string
inspection. stoull("-1") wraps to ULLONG_MAX which fails the
range check naturally. Also catches absurdly large positive values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Simplify getMaxPteEntries per maintainer request

Remove MC_EFA_MAX_PTE_ENTRIES env var override entirely.
The default 22M PTE entries is sufficient for all practical
EFA workloads. Eliminates parsing complexity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove stale env var comment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
93f8e42809
[CI] allow clang-format (generic binary) to be detected as version 20 (#2334)
* fix(scripts): allow clang-format (generic binary) to be detected as version 20

Previously the script only looked for an exact binary named clang-format-20.
Now it also checks the generic `clang-format` binary and verifies its actual
version, supporting Homebrew and other installs where the binary is not suffixed.

* fix(scripts): robustly parse clang-format major version
c1c259e885
fix(ha): ensure /oplog/{cluster_id}/latest key is initialized on first master startup (#2168)
he /latest key initialization in EtcdOpLogStore::Init() was gated by
enable_batch_write_, which is only true for WRITER role instances.
However, the master's snapshot path creates EtcdOpLogStore with default
parameters (enable_batch_write=false), so Init() always skipped the
/latest key creation.

This caused 'key not found' errors on first deployment when snapshot
resolution tried to read /oplog/{cluster_id}/latest.

Fix: Remove the enable_batch_write_ guard. The initialization uses CAS
(Create-if-not-exist) semantics, so concurrent callers are safe — only
the first one actually creates the key.

Signed-off-by: leonzzhu <leonzzhu@tencent.com>
94c58aa492
[Misc] Enhance PR template with AI disclosure and structured testing (#2397)
Add AI assistance disclosure section, expand module list (PG, Common,
P2P Store), add performance improvement type, and require test
commands and results.

Ref: vllm PR template (AI accountability), sglang PR template

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
311473f6a7
[CI/Build] Expand auto-labeling and add PR description cleanup (#2396)
Add labels for P2P Store, Integration, Common, CI/Build, Docs, Tests,
and Ascend/NPU modules. Add bot to clean HTML comments from PR
descriptions.

Ref: vllm new_pr_bot.yml, sglang labeler.yml

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
60e506f87a
[CI] Disable ub_transport_test and fix race condition (#2324)
* [TransferEngine][UB] Fix slice-recycle race causing intermittent hang

Publish per-slice completion (markSuccess/markFailed) only after the
worker has finished reading the slice and updating jetty/jfc depth
counters. Previously markSuccess() was called inside UrmaContext::poll()
while performPoll() still dereferenced the slice afterwards; once
completion was published the main thread could free the batch and recycle
the Slice, so the worker would read/return depth via a reused slice and
corrupt the depth accounting, eventually stalling submits and hanging the
transfer.

* [Build][UB] Do not register ub_transport_test with ctest

ub_transport_test may still have race conditions and other stability
issues, so keep it out of CI for now. Following the existing RDMA
transport test convention, the executable is still built but its
add_test() registration is commented out. It can still be run manually
via ./ub_transport_test.

* [TransferEngine][UB] Localize slice bookkeeping inside UrmaContext::poll

Per review on #2324, restore slice->markSuccess() as the call that
publishes a successful completion (instead of writing slice->status
directly from UbWorkerPool::performPoll). To make that safe, move all
per-slice deref into UrmaContext::poll itself: the jetty_depth
aggregation runs there, and successful slices have markSuccess() called
in place. Only failed slices are returned to performPoll, so the worker
never holds — let alone dereferences — a slice that may have been
recycled by the submitting thread.

This keeps the Slice API (markSuccess / markFailed) and every other
transport untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: format code

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2e1dc5ade2
[TE] Fix NVMeoF loop variable bug and NVMeoFBatchDesc leak (#2373)
* [TE] Fix NVMeoF loop variable bug and NVMeoFBatchDesc leak

- getTransferStatus: fix loop that passes slice_id (constant) instead
  of i (iterator) to desc_pool_->getTransferStatus(), causing every
  iteration to query the same first slice.
- freeBatchID: delete NVMeoFBatchDesc before Transport::freeBatchID,
  fixing per-batch memory leak.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix clang-format: join single-line getTransferStatus call

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
474f67852a
[TransferEngine] Bound handshake-port connect() with a timeout (#2425)
* [TransferEngine] Bound handshake-port connect() with a timeout

A blocking connect() in SocketHandShakePlugin has no deadline:
SO_RCVTIMEO does not apply to connect(), so a connect to an
unroutable address stalls for the kernel's full TCP SYN-retry
cycle, which is minutes with default tcp_syn_retries. During
rolling deployments, torn-down peers leave unroutable IPs, and
these connects run on RDMA worker threads (performPostSend ->
setupConnectionsByActive), where the stall also delays CQ polling
for transfers to healthy peers.

Connect in non-blocking mode and bound the wait with poll(),
checking SO_ERROR for the handshake verdict, then restore blocking
mode for the request/response exchange. Applies to all outbound
handshake-port RPCs (QP handshake, probe, notify, metadata
exchange). Timeout defaults to 5 seconds, tunable via
MC_HANDSHAKE_CONNECT_TIMEOUT.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [TransferEngine] Fail immediately when poll() reports connect timeout

Review feedback: poll() returning 0 already means the timeout
expired; fail with ETIMEDOUT directly instead of looping back to
re-derive it from the clock. Also makes the wait robust to
wall-clock steps, since poll()'s own accounting decides.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
a3ce92c417
[Docs] Readme add pypi npu badge (#2437)
* Add npu badge

* Add npu badge

---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
774af6666a
[TransferEngine] fix: reset last_wait_ts after work to avoid skipping busy-spin phase (#2434)
When a worker thread finishes processing slices and becomes idle again,
last_wait_ts was not reset, causing the next idle period to immediately
exceed the 100ms threshold and skip the busy-spin phase, degrading latency.
ebee6d48bf
feat(store): scope snapshot catalog paths by cluster_id for multi-cluster S3 isolation (#2301)
Thread cluster_id into the SnapshotCatalogStore layer so that all S3
object paths (data, descriptor, latest.txt) are scoped per cluster:
  mooncake_master_snapshot/{cluster_id}/{snapshot_id}/...

Changes:
- Rename kSnapshotRoot to kSnapshotRootBase; add BuildSnapshotRoot(cluster_id)
- All path helper functions now accept snapshot_root parameter
- Add GetSnapshotRoot() pure virtual to SnapshotCatalogStore interface
- EmbeddedSnapshotCatalogStore accepts cluster_id in constructor
- RedisSnapshotCatalogStore computes snapshot_root_ from cluster_namespace_
- MasterService uses catalog_store_->GetSnapshotRoot() instead of hardcoded path
- Backward compatible: empty cluster_id produces original path

Signed-off-by: leonzzhu <leonzzhu@tencent.com>
0c1e5c51d7
[CI] Optimize Ascend CI Test Build Speed (#2439)
Co-authored-by: ZhaoBaiwei <zhaobaiwei@huawei.com>
5673fa5161
[Doc] Fix inaccuracies in EFA transport doc (vLLM router, SGLang patch link, Technical Details) (#2443)
Co-authored-by: EC2 Default User <ec2-user@ip-172-31-8-212.us-west-1.compute.internal>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
5f27aa67dd
[build] Migrate NPU wheel CI to cloud runners with ARM/x86 matrix (#2386)
* [build] Migrate NPU wheel CI to cloud runners with ARM/x86 matrix

- Replace self-hosted runner + Docker container with ubuntu-22.04-arm/ubuntu-22.04 cloud runners
- Remove container, device mappings, volume mounts, and git mirror logic
- Install CANN toolkit from OBS on every build
- Clone and compile HIXL from source for ADXL headers/libs
- Install setuptools for Python 3.12+ compatibility
- Unify ARM/x86 into single matrix: 2 arch x 4 Python versions
- Pre-set PYTHONPATH and CMAKE_PREFIX_PATH to fix CANN set_env.sh
- Use sudo for dependency installation and cmake --install
- Use /usr/local/Ascend/cann symlink path for set_env.sh

* [build] Add repository check to NPU PyPI publish step

---------

Co-authored-by: JieTang <tangjie66@huawei.com>
319afb52bf
[TE][Sunrise][Feat] Enable Sunrise support in the classic transfer engine path (#2290)
* [TE][Sunrise]: Enable Sunrise support in the classic transfer engine path

Signed-off-by: Lancer <maruixiang6688@gmail.com>

* upd

Signed-off-by: Lancer <maruixiang6688@gmail.com>

---------

Signed-off-by: Lancer <maruixiang6688@gmail.com>
45faed0601
[Store] test: add comprehensive MasterAdminServer HTTP endpoint tests (#2431)
* [Store] test: add comprehensive MasterAdminServer HTTP endpoint tests

Add 49 unit tests in master_admin_server_test.cpp covering all 16 HTTP
endpoints served by MasterAdminServer:

Always-available endpoints:
  - /metrics, /metrics/summary, /health, /role, /ha_status, /leader

Service-dependent endpoints:
  - /query_key, /get_all_keys, /get_all_segments, /get_segments_detail,
    /query_segment, /batch_query_keys
  - /api/v1/drain_jobs (create/query/cancel)
  - /api/v1/segments/status

Tests cover: all runtime states, service available/unavailable transitions,
error cases (invalid params, missing keys, nonexistent resources), and
end-to-end drain job lifecycle.
2e62dbac2c
[CI] add qoder review (#2462)
* Create assistant.yml
7b7c2afe6e
[Store] fix: /query_key endpoint returns valid JSON response (#2435)
* [Store] fix: /query_key endpoint returns inconsistent response format

The /query_key endpoint had an inconsistent response format: the error
path (SetServiceUnavailable) returned valid JSON with the correct
Content-Type, but the success path returned NDJSON with text/plain.
This aligns both paths to use the same JSON format and Content-Type.
3dbb8ec6eb
fix(store): check actual disk space in eviction logic (#2419)
When multiple StorageBackend/BucketStorageBackend instances share the
same filesystem (e.g., multiple vLLM TP ranks), each instance tracks
space with independent internal counters. The combined writes can exceed
actual disk capacity before any single counter hits its quota,
so eviction is never triggered and writes fail with ENOSPC.

This fix adds fs::space() checks to both backends:

- File-per-key backend: CheckDiskSpace() verifies actual disk
  availability after the internal quota check. If actual space
  < required + 256MB, returns false to trigger eviction.

- Bucket backend: PrepareEviction() calculates the disk space deficit
  and accumulates estimated freed space (data_size + meta_size) per
  evicted bucket. Due to block alignment, actual disk usage >=
  data_size + meta_size, so this is a safe lower bound that prevents
  under-eviction. The loop stops when either quota is satisfied or
  the accumulated freed space covers the deficit.

Fixes: disk filling to 100% with zero evictions when multiple
instances write to the same disk.
4db9f90b58
[CI] Skip Qoder code review for fork and cross-repo PRs (#2473)
Add a job-level condition so qoder-review only runs when the PR head
repository matches the base repository. This avoids running Qoder on
PRs opened from forks or other repos.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Teng Ma <stmatengss@users.noreply.github.com>
f044bc095a
[Integration] Return documented failure sentinel 0 from uint64 async transfer APIs (#2433)
batchTransferAsync and transferSubmitWrite return batch_id_t (uint64_t)
but returned -1 on openSegment failure, which Python receives as
2**64 - 1. The documented failure sentinel for batch_transfer_async* is
0 (and a uint64 can never satisfy transfer_submit_write's documented
"negative value on failure"), so callers following the docs treat the
bogus id as valid and crash dereferencing it in
get_batch_transfer_status.

Normalize the three failure sites to return 0 (batch ids are heap
BatchDesc pointers, never 0), free the allocated batch on submit
failure in transferSubmitWrite like the sibling paths in
batchTransferAsync/batchTransferSync, and align the
transfer_submit_write doc contract.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
dc54f652d2
fix(store): wrap async main() for console_scripts entry point (#2453)
mc_store_rest_server console_scripts entry point called the
async main() directly, which returned an unawaited coroutine
object instead of running the service.

- Add sync_main() wrapper that uses asyncio.run(main())
- Update pyproject.toml entry point to sync_main
- Both python -m and mc_store_rest_server now work correctly

Co-authored-by: Claude <noreply@anthropic.com>
31dbd93389
Extend standalone same-device avoidance and decouple adxl_compat (#2366)
Standalone thin clients previously applied same-host endpoint offset only
under RoCE. Extend phy_dev mapping and same-host +1 offset to all
non-fabric-mem paths (HCCS/PCIe/RoCE), populate hostIp in segment
metadata, and harden target resolution (ACL context, empty hostIp guard).

Also detect roce_mode from ASCEND_GLOBAL_RESOURCE_CONFIG when
comm_resource_config.protocol_desc contains roce:*.

Inline adxl types into adxl_compat.h, add GetCapability with FeatureType,
and enable AutoConnect by default only when the capability is supported.

Co-authored-by: Developer user <youxiao@huawei.com>
d95a93231c
[Store] Fix local hot cache rejecting larger objects after block reuse (#2466)
LocalHotCacheHandler::SubmitPutTask admitted a slice into a recycled block
by comparing slice.size against block->size. But block->size holds the
*logical* length of the last object stored in the block (and is relied upon
as the object length on the read path), and GetFreeBlock() does not reset it
when a block is reused. As a result, once a block had cached a small object
it would permanently reject any larger object, silently shrinking each block
to the smallest size it ever held and degrading hot-cache hit rate on
mixed-size workloads.

Compare against the fixed block capacity (GetBlockSize()) instead. The
logical-size semantics of block->size are unchanged.

Add a regression test (RecycledBlockAcceptsLargerObject) that fills a
single-block cache with a small object, then submits a larger object to force
block reuse and asserts it is admitted.
2732054200
[Docs] Fix onboarding bugs: NameError, Dockerfile ref, PyPI URL, missing deps (#2408)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Aoi <aione.moe.dev@gmail.com>
0977609b79
[TE][Sunrise][Feat] Enable Sunrise VRAM support in tebench for the TENT backend (#2452)
* [TENT][Sunrise]: Enable Sunrise support in tebench for the TENT backend

Signed-off-by: Lancer <maruixiang6688@gmail.com>

* upd

Signed-off-by: Lancer <maruixiang6688@gmail.com>

* upd

Signed-off-by: Lancer <maruixiang6688@gmail.com>

* upd

Signed-off-by: Lancer <maruixiang6688@gmail.com>

* upd

Signed-off-by: Lancer <maruixiang6688@gmail.com>

---------

Signed-off-by: Lancer <maruixiang6688@gmail.com>
75315dedaf
[Store] Fix stale hot cache reuse after object removal (#2447)
Co-authored-by: Le1zyCatt <148605186+Le1zyCatt@users.noreply.github.com>
Co-authored-by: wangyuqi0429 <82140563+wangyuqi0429@users.noreply.github.com>
1c6d2d721c
[build] Add Python 3.9 support to NPU wheel release (#2483)
- Add '3.9' to python-version matrix in release-npu.yaml
- Add repository check to PyPI publish step
- Matrix now covers 2 architectures x 5 Python versions = 10 wheel combinations

Co-authored-by: JieTang <tangjie66@huawei.com>
d0e4b6a029
[Store] fix: change default eviction policy for offload bucket from none to fifo (#2475) (#2474)
Previously, MOONCAKE_OFFLOAD_BUCKET_EVICTION_POLICY defaulted to "none",
which could cause disk full errors when the offload bucket reached its
capacity limit. Change the default to "fifo" to enable automatic eviction
of oldest data, preventing disk exhaustion and related write failures.

Signed-off-by: tan changzhi <544463199@qq.com>
7ee436c892
fix(efa): declare FI_HMEM in caps for GPU builds (#2448)
The EFA RDM provider routes intra-node transfers through its SHM
sub-provider. For transfers above the SHM inline limit, SHM performs
Segmentation And Reassembly (SAR), copying the payload through a bounce
buffer. Registering device memory with FI_MR_HMEM (mr_mode) alone is
not sufficient: without FI_HMEM in hints->caps the SHM copy callbacks
are initialized in plain-host mode, so SAR does a host memcpy()
directly into a CUDA device virtual address and SIGSEGVs in
__memcpy_avx512_unaligned_erms -- on the send path (smr_copy_to_sar)
or on the CQ-poller receive path (smr_copy_from_sar).

This affects cross-process, same-node GPU-to-GPU transfers (e.g. two
ranks colocated on one node). The same-process self-loopback fast path
(tryLoopbackCopy) does not cover these, since they carry distinct RPC
ports and still route through libfabric -> EFA RDM -> SHM SAR.

Adding FI_HMEM to caps (guarded on USE_CUDA/USE_HIP, matching the
existing FI_MR_HMEM gating) makes the provider wire up HMEM-aware
copies (cudaMemcpy/cuMemcpy) on every path, including SHM SAR. This
keeps the fast intra-node SHM path enabled, unlike the
FI_EFA_ENABLE_SHM_TRANSFER=0 workaround.

Ref: https://github.com/ofiwg/libfabric/issues/12328

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
299e95e70c
[Store] Fix ABBA deadlock between GracefulUnmountScheduler and snapshot_mutex_ (#2486)
GracefulUnmountScheduler::TimerLoop held the scheduler mutex_ while calling
MasterService::UnmountSegment(), which acquires snapshot_mutex_ (shared). That
is the opposite lock order from GracefulUnmountSegment(), which holds
snapshot_mutex_ (exclusive) and then calls Schedule() -> scheduler mutex_. Run
concurrently, each thread waits on the lock the other holds (and the
snapshot_mutex_ writer blocks TimerLoop's reader acquire), so the RPC thread,
the timer thread, and the scheduler's Stop()/join() on shutdown wedge
permanently. GracefulUnmountSegment is a wired RPC, so this is reachable.

Drain the expired records into a local vector (already done) and release the
scheduler mutex_ before invoking UnmountSegment, so TimerLoop never holds the
scheduler lock while acquiring snapshot_mutex_. This removes the inversion.

Verified on Ubuntu: master_service_test 129/129 (incl. the 8 GracefulUnmount
tests that exercise TimerLoop -> UnmountSegment) and segment_test 11/11.
44a6068420
[CI/Build] Add stale issue/PR bot (#2395)
Automatically mark inactive issues (90 days) and PRs (90 days) as
stale, then close them after 30/14 additional days. Exempts pinned,
security, RFC, and WIP items.

Ref: vllm stale.yml, sglang close-inactive-issues.yml

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
c25d40cb33
[Store][Refactor]: extract generic DeadlineScheduler from graceful unmount (#2494)
* refactor(store): extract generic DeadlineScheduler from graceful unmount

Replace the MasterService-embedded GracefulUnmountScheduler with a
reusable, header-only DeadlineScheduler<Id> template that takes a
callback and exposes Schedule/RemoveIf/Stop. MasterService now holds a
DeadlineScheduler<GracefulUnmountDeadlineRecord> and supplies the
unmount logic via a callback. Behavior is unchanged.

Add deadline_scheduler_test.cpp covering deadline ordering, wait
preemption, RemoveIf filtering, Stop cancellation, empty-queue edges,
and already-expired records.

* Optimize deadline scheduler removal
9741cd7575
docs: publish built-in skills and add plugin marketplace (#2497)
Add a Skills section to the documentation site and a Claude Code plugin
marketplace so the built-in skills are discoverable and installable
without cloning the full repository.

- Register mooncake-api as a slash command (add missing SKILL.md frontmatter)
- Add per-skill plugin.json manifests
- Add .claude-plugin/marketplace.json using git-subdir sources so installs
  fetch only the relevant skill subdirectory
- Add docs/source/skills/ pages (overview + per-skill) and link from index
a1e97616df
[Store] support zero-sized tensors in Python APIs (#2470)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Teng Ma <teng-ma@linux.alibaba.com>
4969cfc08a
[Integration] Fix double-free in buffer_to_tensor error path (#2493)
* [Integration] Fix double-free in buffer_to_tensor error path

In the owning path (get_tensor / batch_get_tensor, buffer_handle != null),
buffer_to_tensor allocates exported_data with `new char[]` and hands its
ownership to the numpy array's py::capsule (deleter = delete[]) via
create_typed_array(take_ownership=true). If a later step in the same try block
throws a Python exception -- reshape() with a mismatched shape, from_numpy(),
or .view(float8_*) on a torch build without float8 -- the numpy array is
destroyed during stack unwinding (the capsule runs delete[]) and the catch
block then runs `delete[] exported_data` again -> heap double-free (crash /
corruption). Any error during tensor materialization thus turns a recoverable
failure into a worker crash.

Track whether ownership was transferred to the capsule and only delete[] in the
catch when it was not (throw before/at array construction). The early returns
before the try are unaffected (ownership not yet transferred there).

Verified with a standalone ASAN reproduction using the real pybind11 + numpy
machinery (verbatim create_typed_array + a numpy reshape ValueError after the
transfer): the unfixed logic reports "AddressSanitizer: attempting double-free"
on operator delete[]; the fixed logic catches the ValueError and returns
cleanly.

* [Integration] Use RAII guard for exported_data lifetime (review)

Address review feedback on the buffer_to_tensor double-free fix: replace the
manual ownership_transferred flag with a std::unique_ptr<char[]> guard that is
released once ownership passes to the numpy array's capsule. The catch no
longer deletes exported_data manually -- the guard frees it iff ownership was
not transferred. Behavior is unchanged; this is the more idiomatic, future-proof
form. Re-verified with the ASAN reproduction (unfixed logic double-frees; the
RAII guard returns cleanly).
4580e55aa3
[TE] Add MC_TE_FILTERS env var for IB device whitelist (#2495)
Add MC_TE_FILTERS to restrict IB device discovery in
listInfiniBandDevices(). When set, only devices whose ibv device names
appear in the comma-separated list are considered for topology
construction; all others are skipped with an INFO log.

Example: export MC_TE_FILTERS=mlx5_0,mlx5_1
3c3b01b17f
[Store] Group BatchGetReplicaList metadata lookup by shard (#2508)
* [Store]: group BatchGetReplicaList lookups by shard

Add a batch-aware MasterService::BatchGetReplicaList path that groups keys by metadata shard before accessing metadata.

This is a follow-up to #2405 for the BatchGet RPC path. The implementation preserves response ordering and existing per-key behavior for not-found objects, not-ready replicas, tenant isolation, lease granting, metrics, and promotion-on-hit handling.

Add unit coverage for grouped-key ordering and tenant isolation.

Signed-off-by: Schatten <czhengt@qq.com>
e2d7e8f3a3
[Store] recover etcd client after leader hang (SIGSTOP) (#2383)
* fix(store): recover etcd client after leader hang (SIGSTOP)

Issue: kvcache-ai/Mooncake#2253

When the current etcd leader is suspended with SIGSTOP (kill -19), the
TCP connection remains half-open. Mooncake master detects the lease
keepalive failure and stops serving, but recovery attempts reuse the
same stale storeClient, causing GrantLease/CreateWithLease to timeout
with 'context deadline exceeded'.

Changes:

- Go wrapper: configure gRPC keepalive (10s/3s/PermitWithoutStream) to
detect half-open connections faster; add synchronized getStoreClient()
accessor; add EtcdStoreResetClientWrapper that cancels all active
keepalive/watch contexts, creates a fresh clientv3.Client, and closes
the old one.

- Go wrapper (review fix): cancelAllStorePrefixWatches() no longer
deletes map entries early; let goroutine defer clean up to avoid
race with new watch registration. Watch goroutine now sends
WATCH_BROKEN on all ctx.Done() paths, allowing C++ watchers to
detect reset and reconnect.

- C++ EtcdHelper: add ResetEtcdStoreClient() API.

- C++ HA coordinator: trigger ResetConnection() after GrantLease or
CreateWithLease failures in TryAcquireLeadership, and after
ETCD_OPERATION_ERROR in the keepalive thread.

- C++ HA coordinator (review fix): on CreateWithLease non-transaction
failure, reset the etcd client FIRST before attempting RevokeLease.
This ensures the stale client is always replaced even if RevokeLease
also fails on the same broken connection.

- Tests: add gtest cases verifying basic operations, keepalive
termination, and prefix watch broken/reconnect after reset.
Add e2e bash script for 3-node etcd SIGSTOP and SIGKILL regression
testing.

* Update mooncake-common/etcd/etcd_wrapper.go

Co-authored-by: Yuchen Kou <kouyuchen@approaching.ai>
ae2f6ff684
[Store] Make etcd master-view watch event-driven instead of polling (#2484)
This PR changes etcd `master_view` change detection from periodic polling to an event-driven watch-based mechanism, significantly reducing steady-state read load on etcd while preserving failover responsiveness. The implementation ensures the watch is fully established before the initial read, avoids missing changes between watch setup and read, and safely manages the watch lifecycle with RAII-style cleanup and condition-variable waiting. A short polling fallback is kept for cases where the watch cannot be established.

It also adds real-etcd tests covering watch-driven leader-loss detection, stable-view timeout behavior, and immediate return when the current view has already changed. Follow-up fixes address watch readiness races, a potential callback-context UAF on stop timeout, and stale test comments.


Co-authored-by: silas-scitix <292976869+silas-scitix@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ea37ffa97e
[Store][Refactor]: Refactor master admin service (#2422)
Refactor the Mooncake Store master admin HTTP service out of rpc_service into a dedicated MasterAdminServer implementation.
8bfa404f92
[Store] Rust: don't force-link the ASan runtime in non-sanitized builds (#2510)
Signed-off-by: donghun-furiosa <donghun.lee@furiosa.ai>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
945f3e61c7
[Store][TE] Fix remaining signed-char ::tolower/::toupper UB missed by #2367 (#2488)
* [Store][TE] Fix remaining signed-char ::tolower/::toupper UB missed by #2367

PR #2367 fixed UB from passing a (possibly signed) char to ::tolower/
::toupper via std::transform, but three sibling sites on operator/CLI-
controlled input were missed and remain on main:

- mooncake-transfer-engine/src/transport/tcp_transport/tcp_transport.cpp:
  env MC_TCP_ENABLE_CONNECTION_POOL
- mooncake-store/include/utils.h: size-string unit suffix (env
  MC_MMAP_ARENA_POOL_SIZE, global_segment_size flag, config map).
  Line 274 of this file already uses the safe idiom; line 225 was missed.
- mooncake-store/include/ha/oplog/oplog_store_factory.h: HA
  oplog_store_type config string

A byte > 0x7F sign-extends to a negative int, which is UB for these
functions (argument must be representable as unsigned char or equal
EOF). Apply the same lambda idiom as #2367 and add <cctype> where it
was only available transitively. Behavior is unchanged for ASCII input.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [Store][TE] Annotate case-conversion lambdas with explicit -> char return type

Address review feedback on #2488: std::tolower/std::toupper return
int, so the lambdas implicitly narrow back to char when std::transform
writes them to the std::string output iterator. Strict compiler flags
(-Wconversion / -Wnarrowing on some toolchains) warn on this. Pinning
the return type to char makes the narrowing explicit at the lambda
boundary and silences the warning without changing behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
b92cf05ff7
[TE] Fix signed-char tolower UB in PCI BDF lowercasing loops (follow-up to #2367/#2488) (#2504)
* [TE] Fix signed-char tolower UB in PCI BDF lowercasing loops

Follow-up to #2367/#2488 (requested in #2488): the C-style PCI-BDF
lowercasing loops still pass a (possibly signed) char to tolower, which
sign-extends bytes > 0x7F to a negative int — UB, since the argument
must be representable as unsigned char or equal EOF.

Convert the 7 sites (topology.cpp, tent cuda/rocm probes, tent
memory_prober, benchmark te/tent backends) to
static_cast<char>(std::tolower(static_cast<unsigned char>(*ch))),
matching the idiom from #2367, and add/normalize <cctype> includes.

The strings are driver-generated PCI bus ids (ASCII), so this is a
correctness/consistency cleanup; behavior is identical for ASCII.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [TE] Address review: use a uniform te_lower() helper instead of per-site casts

Per @alogfans's review on #2504/#2488: replace the repeated
static_cast<char>(std::tolower(static_cast<unsigned char>(*ch))) at each
PCI-BDF lowercasing loop with a single inline te_lower() helper added to
common.h. Same well-defined behavior, no more mass typecasts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [TE] Move te_lower() to a lightweight char_util.h to fix tent build

The previous commit put te_lower() in common.h, but pulling common.h into
the TENT TUs collides with tent/common/types.h, which defines
LOCAL_SEGMENT_ID as a macro `#define LOCAL_SEGMENT_ID (0ull)` while
common.h declares `const static int LOCAL_SEGMENT_ID = 0;` — the macro
expands inside the declaration and breaks the build (build-flags CI).

Put te_lower() in a new minimal header char_util.h that only pulls in
<cctype>, and include it at all 7 sites instead of common.h. No collision
with the tent macro, still a single uniform helper per @alogfans's review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* [TE] Name the helper to_lower() to match the std::tolower convention

Rename te_lower() -> to_lower() in char_util.h and the 7 call sites. The
repo has no existing named lowercase helper to reuse, and to_lower reads
naturally alongside std::tolower.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
948e5a8b2a
[TENT] Fix CUDA event leak in nvlink/mnnvl transports (#2507)
* [TENT] Fix CUDA event leak in nvlink/mnnvl transports

startTransfer() created a cudaEvent per submit (shared by all tasks in
that submit) but nothing ever called cudaEventDestroy, so every submit
leaked one event for the process lifetime.

Track the events in a SubBatch-level vector (one push per successful
create) and destroy them in freeSubBatch. Since submitTransferTasks can
be called multiple times on one SubBatch, a SubBatch may own several
distinct events; the vector frees all of them exactly once, avoiding the
double-free that a per-task destroy would cause (the event is shared
across a submit's tasks).

Also check the cudaEventCreateWithFlags return: on failure, mark the
tasks FAILED instead of recording/querying an uninitialized handle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [TENT] RAII event destroy + check cudaEventRecord

Move cudaEventDestroy into ~NVLinkSubBatch()/~MnnvlSubBatch(); Slab<T>::deallocate calls ~T() (the sole free path), so events are released exactly once per free without a manual loop.

Check cudaEventRecord return: on failure, destroy the event, mark tasks FAILED, and return so getTransferStatus never queries an un-recorded event.

Addresses Gemini review comments on #2507.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1f7f71a18a
[TE] Support per-role Ascend protocol for co-located TEs (#2499)
Allow Transfer Engine instances sharing a process to select different
Ascend link protocols by role: a Store-initialized TE can use the
"store" subtree of ASCEND_GLOBAL_RESOURCE_CONFIG (e.g. RoCE) while a
direct/P2P TE uses the top-level config (e.g. HCCS).

- Add transient GlobalConfig::ascend_store_te_init, set via an RAII
  guard in Client::InitTransferEngine for protocol=="ascend".
- ResolveAscendGlobalResourceConfig() resolves the role-specific subtree;
  IsRoceModeEnabled() and initEngines() consume the resolved config.
- Gate transport use_fabric_mem_ on ascend_store_te_init so non-Store TEs
  never inherit fabric memory; free by per-allocation record instead of
  the process-global flag.
- Rename dummy_real_mode_ -> agent_mode_ and add [AscendTE] link logs.
- Add unit tests for the protocol split and fabric gating.

Co-authored-by: lbjyx <youxiao@huawei.com>
ef0312f80a
Add torch 2.12.1 to EP PG build matrix (#2538)
Signed-off-by: Mohammad Miadh Angkad <176301910+mmangkad@users.noreply.github.com>
bdd90cf2fb
[Store] add codec inference and recursive structure expansion (#2521)
* [Store] add codec inference and recursive structure expansion

Add the first layer of DataProto-style structured object encoding
(split from #2050): type-aware codec selection and recursive dict/list
expansion to leaf columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] address review: ndim check, OverflowError, None in list expansion

- _can_tensor: reject mixed-ndim tensor columns
- _can_numeric_scalar: catch OverflowError from np.result_type
- _try_expand_list: allow None items in list expansion
- Add tests for all three fixes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] harden exception handling in codec predicates

- _can_numeric_sequence: catch OverflowError from np.result_type
- _can_json: catch OverflowError and RecursionError from json.dumps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] distinguish absent keys from None values, escape dict keys in paths

Address reviewer feedback on dict expansion fidelity:
- Add MISSING sentinel to distinguish absent dict keys (case B) from
  None values (case A) and None parent rows (case C)
- Add row_mask to _InferredNode to track which rows have real parents
- Escape dict keys containing . [ \ in paths to prevent ambiguity
- Update _non_null to filter both None and MISSING values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* [Store] check JSON serializability across all rows, not just sampled prefix

_can_json previously only validated the first 128 rows. A non-serializable
value past that index would cause the encoder to fail after inference
accepted the column.  Now every non-null row is validated; only the
byte-size estimate is sampled.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1253d81464
[TE][Fix] Fix excessive memory allocation in submitPostSend (#2490)
The wr_list and sge_list vectors were allocated with size equal to
the entire requested slice count (slice_list.size()). However, each
QP iteration reuses these arrays from index 0, and ibv_post_send
always starts from wr_list.data(). The actual maximum usage per QP
is bounded by max_wr_depth_ (default 256).

For large transfers (e.g., 55GB with default 64KB slice size), this
caused allocation of ~112MB per submitPostSend call (877,952 × 128B),
leading to 17-23ms overhead per call and creating a feedback loop
where the growing queue caused even larger allocations, ultimately
resulting in transfer timeouts.

Fix: Allocate wr_list/sge_list with size = min(max_wr_depth_,
cq_remaining, requested) instead of the full requested count.
This reduces allocation from ~112MB to ~32KB (256 entries).
2bae8b3705
fix(efa): widen MR keys to 64-bit to avoid fi_mr_key() truncation (#2564)
EFA's libfabric provider returns 64-bit memory-region keys via
fi_mr_key(), but BufferDesc::{lkey,rkey} and Transport::Slice::{source_lkey,
dest_rkey,dest_rkeys} stored them as uint32_t, truncating the upper 32 bits
on both the metadata-encode path and the fi_read/fi_write submit path.

In practice EFA keys have been small monotonic values that fit in 32 bits,
so this latent bug has not yet manifested, but it is incorrect.

Inspired by #2535 (HPE Slingshot/cxi backend), which hit the same issue and
widened the CXI path. This change applies the analogous fix to EFA, guarded
by USE_EFA so RDMA verbs builds (whose ibv keys are genuinely 32-bit) are
byte-for-byte unchanged.

Verified on a P5EN-1 <-> P5EN-2 pair (p5.48xlarge, 32 EFA NICs) with
transfer_engine_bench, A/B against unmodified main:
  GPU-to-GPU: baseline 362.44 GB/s vs fixed 363.38 GB/s
  CPU-to-CPU: baseline 35.28 GB/s  vs fixed 37.70 GB/s
EFA unit tests (efa_gpu_loopback_test, efa_transport_test, transport_uint_test,
efa_c_api_test) all pass. No regression.

Co-authored-by: whn09 <whn09@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ad5a82033b
[Doc] complete Rust API reference for transfer engine and mooncake store (#2547)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Teng Ma <stmatengss@users.noreply.github.com>
826ef02ea9
[Store][K8s-Native][3/N] Label-based routing (#2537)
Signed-off-by: Vladislav Nosivskoy <vladnosiv@gmail.com>
208d105e6c
Update BatchAsync for MMNVL (#2384)
* Update BatchAsync for MMNVL

- Update BatchAsync implementation for multi-node NVLink transport
- Fix BatchAsync for CUDA 12.8 compatibility
- Revert srcAccessOrderStream for BatchAsync attr
- Using Mooncake namespace for struct definition
- Minor fixes for BatchAsync

* Align format with Mainstream

---------

Co-authored-by: 百麒 <yaozhong.lyz@alibaba-inc.com>
ac70e68661
[TENT] add local runtime queue dispatch (#2562)
* split submit transfer planning

* guard batch lifetime during submit

* rename prepared submit helpers

* simplify retained submit cleanup

* guard submit batch refs

* add runtime queue knobs

* wire local runtime queue dispatch

* route staging internals through queue

* test runtime queue dispatch

* retain specific terminal queue status

* cover timeout terminal status in admission queue

* poll merged queue owners from derived tasks

* refill runtime queue dispatch window

* let progress worker refill runtime queue

* finish queue owners after state update

* honor rdma device mask in queued dispatch

* reject invalid queue dispatch windows

* drive runtime queue from progress worker

* wake runtime queue from transfer progress

* share batch lifetime state across threads
3a8930403b
[Store] fix integer overflow in BatchOffload for objects larger than 4 GiB (#2570)
OffsetAllocatorStorageBackend::BatchOffload summed slice sizes into a uint32_t
value_size. For an object whose total size exceeds 4 GiB this overflows: the
record is allocated from the wrapped (too-small) value_len, but the write step
then emits the full original slices, so vector_write runs past the allocation
-- a heap buffer overflow / arena corruption. (The following size_t record_size
does not help, because value_len was already truncated.)

RecordHeader keeps value_len as a uint32_t (8-byte on-disk header) by design, so
rather than change the format, sum in 64 bits and skip any key whose value
exceeds 4 GiB -- matching the existing per-key continue-on-failure model --
instead of corrupting memory.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
3b18032803
[TE] HipTransport: restore caller device after async transfer (#2566)
* HipTransport: restore caller device after async transfer

startAsyncTransfer() switches the active device to the source GPU via
setDeviceContext() and enqueues the copy on a stream bound to that device, but
never restores the caller's device before returning. The host thread is left
pinned to the source GPU, so the next kernel launched on that thread (e.g. a
PyTorch op in a TP worker driving intra-node PD KV transfer) targets the wrong
GPU and fails with hipErrorInvalidDevice.

Save the active device at entry and restore it on every exit path, mirroring the
restore already done in setupP2PAccess().
b57d18a471
[TransferEngine] Apply configured SL/TC to the TENT notification QP (#2526)
* [TransferEngine] Apply configured SL/TC to the TENT notification QP

The TENT RDMA data-path QP picks up `params_->service_level` and
`params_->traffic_class` (endpoint.cpp setupOneQP), but the control-path
notification QP hard-codes `ah_attr.sl = 0` and
`ah_attr.grh.traffic_class = 0` in setupNotifyQpConnection.

As a result, when a user configures an SL/TC for QoS isolation (e.g. to
steer Mooncake traffic into a dedicated Virtual Lane away from EP
all-to-all on a shared NIC), the KV-arrival notification messages still
leak onto the default lane, so the isolation is incomplete.

Thread the endpoint's service_level / traffic_class through
setupNotifyQpConnection so the notification QP follows the same fabric
QoS settings as the data QPs.

Behavior-preserving: both fields default to 0 in RdmaParams, so an
unconfigured deployment sets sl=0 / traffic_class=0 exactly as before.

* [TransferEngine] Default SL/TC to 0 in setupNotifyQpConnection signature

Apply review suggestion from @stmatengss: give service_level and traffic_class default values (0) in the forward declaration of setupNotifyQpConnection so existing call sites that do not yet pass these parameters keep compiling unchanged.

No behavioral change: a value of 0 matches the prior hard-coded default.

* [TransferEngine] Map MC_IB_SL env var to TENT service_level config

config.cpp already maps MC_IB_TC -> transports/rdma/endpoint/traffic_class,
and SET_ENDPOINT(service_level, ...) already reads
transports/rdma/endpoint/service_level from config, but there was no
environment-variable entry for service_level (it could only be set via the
config file).

Add the symmetric MC_IB_SL -> transports/rdma/endpoint/service_level
mapping so the TENT transport exposes the same env knob as the main
transfer-engine does in #2525. Together with the notification-QP fix in
this PR, SL is now configurable end-to-end via MC_IB_SL on both data and
control QPs.

---------

Co-authored-by: catyans <catyans@users.noreply.github.com>
0ce447ece5
[Store] add structured object copy-mode policy (#2472)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
be1ceba6c2
[Store]: Fix BatchEvict Over-Eviction Due to Inflated Target When SSD Offload Is Enabled (#2286)
* add disk_object_count in MetadataShard

* clean mixed code

* skip disk-only shard

* skip disk-only shard

* use MetadataIter as candidate

* parallel scanning MetadataShard

* merge main into fix/object-count-eviction

* fix merge error

* fix: rePublishRpcMetaEntry always falls through to PUT due to jsoncpp type-sensitive comparison

* code format

* fix OnDiskReplicaRemoved

* fix OnDiskReplicaRemoved

* code format

* fix merge err

* correct eviction_base count

* Remove duplicate OnDiskReplicaRemoved() call && BatchEvict Phase 1 re-validation fallback

* treat evict_num as a minimum

* code format

* try shard_evictable_count by can_evict_replicas

* try shard_evictable_count by can_evict_replicas

* merge branch main

* merge branch main

* fix merge conflict

* code format

* prevents the eviction thread from modifying metadata between restore and the second persist

* tenants.erase clear empty tenants

* deal merge conflict

* recovery TestSnapshotAndRestore

* merge main

* code format

---------

Co-authored-by: ruanzhao <ruanzhao@kingsoft.com>
Co-authored-by: Claude Opus 4.7 <noreply@owtffssent.com>
6cc11d35e8
[Bugfix] Preserve empty values in Mooncake Store REST GET (#2587)
Co-authored-by: VectorPeak <VectorPeak@users.noreply.github.com>
4874173b22
[TE] Universal TCP Force Mechanism for All Environments (#2593)
* [TE] Force TCP for all environments

* Reformat
68bd6b84f0
[Build] Fix build failure on Python 3.14t without --enable-shared (#2553)
* [Build] Fix build failure on Python 3.14t without --enable-shared

* Remove redundant if(WIN32) guards since Mooncake does not support Windows
fab74eac86
[Store] Add SSD free-ratio-first allocation strategy (#2450)
* [Store] feat: Add SSD free-ratio-first allocation strategy

Add SsdFreeRatioFirstAllocationStrategy that ranks candidate segments by SSD free ratio and preferentially allocates to segments with more available SSD space, enabling balanced SSD utilization across nodes.

  Key additions:
  - SsdMetricsProvider interface for querying per-segment SSD metrics
  - SsdFreeRatioFirstAllocationStrategy with SSD-ratio-based ranking
  - SSD usage tracking (ssd_used_bytes) in LocalDiskSegment
  - gtest unit tests integrated into allocation_strategy_test.cpp
  - Design document (ssd-free-ratio-first-allocation.md)

* [Test] Simplify SsdFreeRatioFirstLoadBalancingDistribution test

  Use equal-sized DDR segments (3 x 64MB) to isolate the SSD free ratio
  effect, removing DDR capacity as a confounding variable. Increase
  allocation count from 500 to 1500 for statistically meaningful results.
  Remove verbose cout diagnostics and simplify assertions to directly
  compare allocation counts across segments with different SSD free ratios.

---------

Co-authored-by: syliudf <syliudf@gmail.com>
Co-authored-by: Le1zyCatt <2490162471@qq.com>
Co-authored-by: Le1zyCatt <148605186+Le1zyCatt@users.noreply.github.com>
7e83a8da07
[TransferEngine] Make InfiniBand Service Level configurable via MC_IB_SL (#2525)
The RDMA endpoint hard-codes `attr.ah_attr.sl = 0`, so Mooncake traffic
cannot be steered into a dedicated Virtual Lane for QoS isolation. On a
shared NIC (e.g. MoE inference where Expert-Parallel all-to-all and
KV-cache transfers contend for the same RDMA NIC), there is no way to
separate the two flows at the SL/VL level.

Peer libraries already expose this knob: NCCL `NCCL_IB_SL`, UCX
`UCX_IB_SL`, NVSHMEM `NVSHMEM_IB_SL`, DeepEP `EP_OVERRIDE_RDMA_SL`.
Mooncake was the only one on the KV-transfer path without it. (Note
`MC_IB_TC` only sets `ah_attr.grh.traffic_class`, which does not drive
VL selection on native InfiniBand.)

This mirrors the existing `MC_IB_TC` implementation:
- add `ib_service_level = -1` to GlobalConfig (-1 = use default 0);
- parse `MC_IB_SL`, validated to the InfiniBand SL range 0-15;
- apply it to `attr.ah_attr.sl` only when configured;
- log it in dumpGlobalConfig.

Fully backward compatible: when MC_IB_SL is unset the SL stays 0,
exactly as before. Adds unit tests for the env-var parsing
(default/valid/boundary/out-of-range/negative/non-numeric) and
documents the knob.

Closes #2515

Co-authored-by: catyans <catyans@users.noreply.github.com>
f9bca599be
[TE] Skip redundant Disconnect on AutoConnect transfer failures (#2604)
When ASCEND_AUTO_CONNECT is enabled, ADXL already tears down the link
via DisconnectOnError. Mooncake now avoids a second Disconnect on sync/async
submit failures, uses forgetConnectedSegment after GetStatus failures, and
still disconnects on application-level async timeouts and deregister paths.

Co-authored-by: lbjyx <youxiao@huawei.com>
25680f27d4
fix(rdma): restore task.request association in submitTransfer (#2610)
RdmaTransport::submitTransfer(BatchID, entries) was simplified in #772 to
delegate slice construction to submitTransferTask(), but the rewrite dropped
the per-entry assignment of task.request. submitTransferTask() then runs
`assert(task.request); auto &request = *task.request;`, so every task it
receives from this entry point carries a null request pointer.

The overload is reached through HeterogeneousRdmaTransport::submitTransfer,
which forwards CPU-source and staged-host batches straight to
transport_->submitTransfer(batch_id, entries). With the missing assignment
that dereferences a null pointer (assert failure in debug, UB/segfault in
release).

Repopulate batch_id and request for each newly created task before
delegating, matching how MultiTransport::submitTransfer and TcpTransport
associate each task with its originating request.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
11182dce14
[MUSA] Add release-musa github workflow (#2576)
Signed-off-by: Xiaodong Ye <xiaodong.ye@mthreads.com>
0daf794fc4
[Store] add structured object flat mvp (#2600)
* [Store] add flat structured object tensor support

Add object-level structured transfers for flat mappings and wrapped values, with torch tensor fields using the Store tensor fast path when available.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1c9ae70274
[TE] Enforce one-shot lifecycle for connected RDMA endpoints (#2588)
* [TE] Enforce one-shot lifecycle for connected RDMA endpoints

* Add wr_depth_list_ validation

* Fix based on comments

* Change outstanding CQ check per thread
a06c866da9
fix(maca): map cudaStreamQuery for the intra-node NVLink build (#2606)
* fix(maca): map cudaStreamQuery for the intra-node NVLink build

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>

* ci: retrigger CI after runner disk-space failures

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>

---------

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
077b696f8a
[Store] Make offloading_queue_limit and offload_cap_ratio configurable via gflags (#2599)
Previously offloading_queue_limit was hardcoded to 50000 and kOffloadCapRatio was a constant 0.5, resulting in an offload_cap of only 25000 keys per eviction cycle. Keys exceeding this cap were force-evicted (discarded) instead of being offloaded to SSD, causing NVMe to remain idle and KV cache hit rate to suffer.

This commit:
- Adds gflags --offloading_queue_limit (default 50000) and --offload_cap_ratio (default 0.5, range [0,1]) to master.cpp
- Propagates both parameters through the full config chain: MasterConfig -> MasterServiceSupervisorConfig -> WrappedMasterServiceConfig -> MasterServiceConfig -> MasterService
- Replaces the constexpr kOffloadCapRatio with a member variable offload_cap_ratio_
- Adds a gflags validator for offload_cap_ratio range
- Updates mooncake-store deployment guide with new flag descriptions and usage example

Usage example:
  --offloading_queue_limit=500000 --offload_cap_ratio=0.8
  => offload_cap = 400000 (vs previous fixed 25000)
0f078b105a
Infer gpu device from request source pointers, set correct device for stream creation. (#2569)
* Infer gpu device from request source pointers, set correct device context for stream.

* Infer gpu device from request source pointers, set correct device for stream creation.

* Infer gpu device from request source pointers, set correct device for stream creation.

* Infer gpu device from request source pointers, set correct device for stream creation.

---------

Co-authored-by: wjy <wjy494255@alibaba-inc.com>
af26a48c03
[CI] Add AWS EFA wheel build/release to official CI/CD (#2565)
* ci: add AWS EFA wheel build/release to official CI/CD

Adds EFA (libfabric) wheel variants to the wheel pipeline, mirroring the
existing non-cuda / cuda13 / npu variant pattern. The EFA transport memory
path is CUDA-aware (FI_HMEM_CUDA / GPUDirect under USE_CUDA=ON, FI_HMEM=system
otherwise), so two PyPI packages are produced:

  mooncake-transfer-engine-efa            USE_EFA=ON USE_CUDA=ON  (GPU)
  mooncake-transfer-engine-efa-non-cuda   USE_EFA=ON USE_CUDA=OFF (CPU/DRAM)

No EFA hardware is needed to build: USE_EFA only needs libfabric headers/lib
to compile and link, which the stock ubuntu runner gets from the distro
libfabric-dev package. auditwheel already excludes libfabric/libefa from the
wheel (build_wheel.sh) so they resolve to the user system AWS EFA install at
runtime, avoiding the dual-libfabric / device-claim conflict. The distro
libfabric (1.x) is ABI-forward-compatible with the AWS EFA libfabric (2.x)
loaded at runtime; the EFA transport uses only long-stable fi_* APIs.

Changes:
- scripts/build_wheel.sh: add EFA_BUILD / EFA_NON_CUDA_BUILD variants that
  rename the package to mooncake-transfer-engine-efa{,-non-cuda}.
- .github/workflows/ci_efa.yml: PR-time build validation (one python per
  variant), wired into ci.yml CI Gate. Verifies libfabric is NOT bundled.
- .github/workflows/release-efa.yaml: tag-triggered full python matrix
  (3.10-3.13) x {cuda, non-cuda}, publishes to GitHub Release + PyPI.

Verified end-to-end on a p5.48xlarge: both variants build, produce the
correct package names, and exclude libfabric from the wheel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(efa): document PyPI install and EFA wheel CI/CD

Now that EFA wheels are published by the release pipeline, document the
recommended path (pip install mooncake-transfer-engine-efa / -efa-non-cuda)
before the build-from-source instructions, and note the EFA_BUILD /
EFA_NON_CUDA_BUILD variant env vars plus the ci_efa.yml / release-efa.yaml
workflows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci(efa): split EFA release into per-package workflows; bump CUDA to 12.8.1

Address review feedback on PR #2565:

- Split release-efa.yaml (which built both cuda + non-cuda via a matrix.variant)
  into two standalone workflows, one per PyPI package, mirroring the existing
  release.yaml / release-non-cuda.yaml pattern:
    * release-efa.yaml          -> mooncake-transfer-engine-efa (USE_CUDA=ON)
    * release-efa-non-cuda.yaml -> mooncake-transfer-engine-efa-non-cuda (USE_CUDA=OFF)
  Each has its own build matrix, libfabric-exclusion check, artifact pattern, and
  publish-release/PyPI step, so a package publishes from a dedicated workflow
  (per-package trusted publisher / artifact pattern).

- Bump CUDA Toolkit 12.4.1 -> 12.8.1 in ci_efa.yml and release-efa.yaml to match
  the version used across the rest of the repo workflows (ShangmingCai review).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: whn09 <whn09@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
b2c2583252
[TE] Fix signed-char isxdigit UB in EFA smaps page-size parsing (follow-up to #2504) (#2619)
detectBufferPageSize() walks /proc/self/smaps and tests the first byte of
each line with std::isxdigit(line[0]) to find VMA header lines. line[0] is a
plain char read from std::getline; on platforms where char is signed, a byte
> 0x7F becomes a negative int. Passing that to std::isxdigit is undefined
behavior -- the argument must be representable as unsigned char or equal EOF.

This is the same class of bug fixed for the PCI BDF lowercasing loops in
#2367/#2488/#2504 (which added char_util.h::to_lower). That sweep did not
reach efa_transport.cpp; this is the last remaining un-cast ctype call in
mooncake-transfer-engine / mooncake-store. Kept the fix inline rather than
routing through a helper since it is a single classification site (isxdigit),
unlike the multi-site case conversion that warranted to_lower().

No behavior change for the realistic input -- smaps header lines start with an
ASCII hex address and attribute lines with ASCII keywords -- so this hardens
against UB without altering parsing.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
59f62c3686
fix(transport): associate task.request in EFA/Kunpeng submitTransfer (#2617)
EfaTransport::submitTransfer(BatchID, entries) and
UbTransport::submitTransfer(BatchID, entries) resize the batch task list
and then hand every slot to submitTransferTask() without setting
task.request on the newly created tasks. submitTransferTask() runs
`assert(task.request); auto& request = *task.request;`, so each task from
this entry point carries a null request pointer (assert failure in debug,
UB / segfault in release).

This is the same defect #2610 fixed in RdmaTransport. After that change,
EFA and Kunpeng UB are the only two transports whose submitTransfer
override still omits the per-entry association that MultiTransport,
TcpTransport, and (post-#2610) RdmaTransport already perform.

Repopulate batch_id and request for each newly added entry before
delegating, and submit only the new tasks instead of re-pushing the whole
list, matching #2610.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
a4de8b2bfa
[Store] add remote tensor batch interfaces (#2050)
Keep the DataProto helper as a thin structured-object adapter while routing tensor-object transfers through BufferPool-backed tensor buffers and safer non-tensor codecs.

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Teng Ma <stmatengss@gmail.com>
3c4fbea862
[Bugfix] Parse string booleans for enable_ssd_offload in from_file (#2506)
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
5bec76df29
[Bugfix] Return HTTP 500 when is_exist reports an error in handle_exist (#2602)
Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
cd05d2693a
[CI] Disable pip cache in build-flags to avoid disk exhaustion (#2626)
The build-flags job builds many configs on one runner, including the EP step
that pip-installs all 5 torch versions. Their wheel cache (~several GB) is never
reclaimed and tips the runner into 'No space left on device' on the later TENT
build. Set PIP_NO_CACHE_DIR so the installs don't retain cached wheels.

Signed-off-by: Michael Goin <mike.goin12@gmail.com>
30f01bdbbb
Fix RDMA simultaneous-open RPC timeout reuse (#2624)
Co-authored-by: leichao.lc <leichao.lc@antgroup.com>
617a01f5bf
[TENT] Add optional Request.deadline_ns and MLU observability metric (#2618)
This is the minimal first step of RFC #2519 (deadline-aware admission),
scoped per @alogfans's review:
"LGTM for the minimal first step (deadline_ns field + MLU observability
only). Let's see the MLU metrics on real workloads before adding policy
logic."

It adds the deadline plumbing and an observability-only feasibility
metric, with NO admission, scheduling, or degradation logic.

- types.h: add optional `uint64_t deadline_ns = 0` to Request. 0 (default)
  is bit-for-bit today's behavior; nothing reads it unless the caller sets
  it. It is an absolute steady_clock timestamp in nanoseconds.

- tent_metrics: add `recordDeadlineMLU(double)` and a `deadline_mlu`
  histogram. MLU (laxity feasibility ratio) is stored in per-mille
  (MLU x 1000) so it can use the same integer observe() as the other
  histograms; the 1000 boundary is MLU == 1.0 (< 1000 met the deadline,
  >= 1000 missed it). Both the enabled and the compile-time-disabled
  (TENT_METRICS_ENABLED=0) stubs are provided.

- transfer_engine_impl: at task completion, when the request carried a
  deadline, emit the post-hoc MLU = actual_transfer_time / window, where
  window = deadline - submit_time. This reuses the existing start_time /
  latency_seconds already computed at the completion point — no new
  measurement subsystem. Behavior is unchanged when deadline_ns == 0.

Per-device EWMA-bandwidth-based *predictive* MLU at submit time (RFC §2)
is intentionally left out of this PoC, because device pinning happens
after submit; this post-hoc MLU validates whether the signal is useful on
real workloads first, as requested.

Note: not built locally (no TENT toolchain on my dev machine); relying on
CI. Change is additive and behavior-preserving when deadline_ns == 0.

Co-authored-by: catyans <catyans@users.noreply.github.com>
747003c058
[Store] Guard MC_MS_AUTO_DISC parsing against std::stoi throwing (#2629)
get_auto_discover() reads the MC_MS_AUTO_DISC env var and parses it with
std::stoi, but only handles the case where stoi succeeds and returns a
value other than 0 or 1 (it warns and falls back to the default). A value
that std::stoi cannot parse - a non-numeric string like "true", an empty
string, or an out-of-range number - makes std::stoi throw
std::invalid_argument / std::out_of_range. Neither get_auto_discover()
nor its caller InitTransferEngine() catches it, so a typo in the env var
aborts client initialization instead of warning and using the default,
which is what the existing branch already intends for bad input.

Wrap the parse in try/catch and route the throwing cases through the same
warning-and-default path, so any invalid MC_MS_AUTO_DISC value degrades
gracefully regardless of whether stoi rejects it or returns it.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
98ca259444
[Bugfix] Set Ascend context in batch get worker (#2557)
* [Store] set Ascend context in batch get worker

* [Store] address batch get context review

---------

Co-authored-by: xianghang7 <xianghuang7@iflytek.com>
e98e90fc9e
[TransferEngine] Guard MC_TCP_SLICE_SIZE parsing against std::stoull throwing (#2641)
* [TransferEngine] Guard MC_TCP_SLICE_SIZE parsing against std::stoull throwing

getChunkSize() reads the MC_TCP_SLICE_SIZE env var and parses it with
std::stoull, but only handles the case where stoull succeeds and returns a
positive value (otherwise it falls back to the 64KB default). A value that
std::stoull cannot parse - a non-numeric string, an empty string, or an
out-of-range number - makes std::stoull throw std::invalid_argument /
std::out_of_range. The parse runs inside the static-local initializer, and
neither getChunkSize() nor the transfer paths that first call it (TcpTransport
read/write) catch the exception, so a typo in the env var aborts the transfer
instead of warning and using the default.

Wrap the parse in try/catch and route both the throwing case and a
non-positive value through the same warning-and-default path, mirroring the
existing MC_MS_AUTO_DISC guard (#2629) and the env parsing in config.cpp
(MC_PKEY_INDEX / MC_IB_TC / MC_IB_SL), so any invalid MC_TCP_SLICE_SIZE value
degrades gracefully.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>

* clang-format the new MC_TCP_SLICE_SIZE warning logs

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>

---------

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
aa9e5740e2
[Doc] docs: update README hardware partners table and logos (#2654)
Replace the HTML partners table with a Markdown layout, host Cambricon,
MetaX, and T-Head logos locally, standardize logo sizing, and refresh
the NVIDIA logo asset.

Co-authored-by: Ke Yang <yangke@approaching.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
69ba77e434
[Store] Fix source refcnt leak in CopyEnd/MoveEnd on invalid source (#2628)
CopyStart/MoveStart call source->inc_refcnt() to pin the source replica
during the transfer, and the CopyEnd/MoveEnd success paths release it with
dec_refcnt(). The error path taken when the source becomes invalid
mid-transfer (source non-null but has_invalid_mem_handle(), or
!is_completed()) returns early without decrementing, so the inc_refcnt()
is never balanced. The branch erases the copy/move targets, not the
source, so the source replica stays pinned and can never be evicted.

Release the refcnt for a non-null source on the error path, mirroring the
existing CopyRevoke/MoveRevoke handling.
89e8cd6652
Hca peer affinity (#2616)
* Add minimal HCA peer affinity primitives

* Add RDMA slice affinity logging

* Precompute resolved HCA peer affinity by local NIC

* Format HCA peer affinity changes

* Remove GPU HCA affinity override

Drop MC_GPU_HCA_AFFINITY in favor of MC_CUSTOM_TOPO_JSON and keep HCA peer affinity precomputation limited to cuda topology entries.

* Resolve topology merge conflict

* Avoid config log merge conflict

* Log RDMA slice affinity at verbose level
8cc493dacc
[TE] Guard against null endpoint_store_ in UrmaContext destructor (#2627)
UbContext::doConstruct() assigns endpoint_store_ only after the virtual
construct() succeeds, returning early otherwise. UrmaContext::construct()
returns ERR_CONTEXT on the normal device-init failures (openDevice,
urma_create_jfce/jfc/jfr), leaving endpoint_store_ as a null shared_ptr.
The destructor then calls endpoint_store_->destroy() unconditionally,
dereferencing null when device initialization failed.

Guard the call like the sibling members in the same destructor, which are
already null-safe (worker_pool_.reset() and the urma_context_ check).
9918790234
[TE] add HPE Slingshot (cxi) backend (#2535)
* [TE] add HPE Slingshot (cxi) backend

* [TE] fix bugs in cxi and add some tests

* [TE] cxi, fix format & typos, remove EFA references

* fix typos.toml to include HPE
fac9d09d83
[TENT] Fix stale getTransferStatus in nvlink/mnnvl/ascend transports (#2505)
These three transports snapshot the output `status` BEFORE polling the
hardware (cudaEventQuery / hixl GetTransferStatus). When the poll
transitions the task to COMPLETED/FAILED/TIMEOUT, only the task is
updated; `status` keeps the pre-poll PENDING value, so the caller sees
the completion one poll cycle late.

Move the `status = TransferStatus{...}` read to after the poll on every
return path. Same class of bug already fixed for io_uring/gds.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4e0d320ac2
[EP] Fix: Skip inactive ranks in combine reduction loop (#2653)
Signed-off-by: Lancer <maruixiang6688@gmail.com>
d64b9e29ee
[Docs] Add Docker Badge for Readme (#2670)
---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
6b98d67d8a
[TE] Name AscendDirectTransport worker threads (#2672)
* [TE] Name AscendDirectTransport worker threads

Set each AscendThreadPool worker thread name to "ascend-wk-<i>" via
pthread_setname_np on Linux so they are identifiable in top -H / ps -L
/ perf instead of inheriting the process name. No-op on non-Linux.

* [TE] Simplify AscendThreadPool thread naming

Drop SetAscendThreadName helper and __linux__ guards; call
pthread_setname_np directly like hixl. Thread names ascend-wk-<i> stay
within the 15-byte pthread limit (pool size <= 16).

---------

Co-authored-by: lbjyx <youxiao@huawei.com>
1cb66e83c1
[Docs] [6/N] Refactor Readme: Streamline README and move setup/trace details to other places (#2652)
---------

Co-authored-by: Ke Yang <yangke@approaching.ai>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
f4f7fd4a03
[Store] Skip bucket files with non-numeric names instead of aborting Init (#2651)
BucketStorageBackend::Init() parses each scanned filename's stem with
std::stoll at two sites. The whole method body is wrapped in one
try/catch that returns INTERNAL_ERROR, so a single stray file with a
non-numeric stem (a temp/backup/renamed file still carrying the bucket
extension) makes std::stoll throw and aborts the entire store init.

Guard each std::stoll and continue past the offending entry, mirroring
the existing skip-and-continue handling already used in both loops and
the env-parse fix in #2629.

Signed-off-by: Yufeng He <40085740+he-yufeng@users.noreply.github.com>
Auto Label PRs / triage (pull_request) Failing after 1m19s Details
Build & Test (Linux) / build (3.10) (pull_request) Has been cancelled Details
Build & Test (Linux) / build (3.12) (pull_request) Has been cancelled Details
Build & Test (Linux) / build-musa (pull_request) Has been cancelled Details
Build & Test (Linux) / build-arm64 (3.10) (pull_request) Has been cancelled Details
Build & Test (Linux) / build-arm64 (3.12) (pull_request) Has been cancelled Details
Build & Test (Linux) / test-wheel-ubuntu (3.10, ubuntu-22.04) (pull_request) Has been cancelled Details
Build & Test (Linux) / test-wheel-ubuntu (3.10, ubuntu-24.04) (pull_request) Has been cancelled Details
Build & Test (Linux) / test-wheel-ubuntu (3.12, ubuntu-22.04) (pull_request) Has been cancelled Details
Build & Test (Linux) / test-wheel-ubuntu (3.12, ubuntu-24.04) (pull_request) Has been cancelled Details
Build & Test (Linux) / build-flags (3.10) (pull_request) Has been cancelled Details
Build & Test (Linux) / build-flags (3.12) (pull_request) Has been cancelled Details
Build & Test (Linux) / Build Docker Image (pull_request) Has been cancelled Details
Build & Test (Linux) / Spell Check with Typos (pull_request) Has been cancelled Details
Build & Test (Linux) / Check code format (pull_request) Has been cancelled Details
Build & Test (Linux) / Check Sphinx docs build (pull_request) Has been cancelled Details
Build & Test (Linux) / check-paths (pull_request) Has been cancelled Details
Build & Test (Linux) / build-wheel-cu13 (pull_request) Has been cancelled Details
Build & Test (Linux) / build-wheel-efa (pull_request) Has been cancelled Details
Build & Test (Linux) / ascend-test (pull_request) Has been cancelled Details
Build & Test (Linux) / integration-test (pull_request) Has been cancelled Details
Build & Test (Linux) / CI Gate (pull_request) Has been cancelled Details
Qoder Auto Code Review / qoder-review (pull_request) Has been cancelled Details
b0db002089
Store: reduce PR4 eviction policy diff noise
kancel closed this pull request 2026-07-06 19:54:46 +08:00
Some checks failed
Auto Label PRs / triage (pull_request) Failing after 1m19s
Build & Test (Linux) / build (3.10) (pull_request) Has been cancelled
Build & Test (Linux) / build (3.12) (pull_request) Has been cancelled
Build & Test (Linux) / build-musa (pull_request) Has been cancelled
Build & Test (Linux) / build-arm64 (3.10) (pull_request) Has been cancelled
Build & Test (Linux) / build-arm64 (3.12) (pull_request) Has been cancelled
Build & Test (Linux) / test-wheel-ubuntu (3.10, ubuntu-22.04) (pull_request) Has been cancelled
Build & Test (Linux) / test-wheel-ubuntu (3.10, ubuntu-24.04) (pull_request) Has been cancelled
Build & Test (Linux) / test-wheel-ubuntu (3.12, ubuntu-22.04) (pull_request) Has been cancelled
Build & Test (Linux) / test-wheel-ubuntu (3.12, ubuntu-24.04) (pull_request) Has been cancelled
Build & Test (Linux) / build-flags (3.10) (pull_request) Has been cancelled
Build & Test (Linux) / build-flags (3.12) (pull_request) Has been cancelled
Build & Test (Linux) / Build Docker Image (pull_request) Has been cancelled
Build & Test (Linux) / Spell Check with Typos (pull_request) Has been cancelled
Build & Test (Linux) / Check code format (pull_request) Has been cancelled
Build & Test (Linux) / Check Sphinx docs build (pull_request) Has been cancelled
Build & Test (Linux) / check-paths (pull_request) Has been cancelled
Build & Test (Linux) / build-wheel-cu13 (pull_request) Has been cancelled
Build & Test (Linux) / build-wheel-efa (pull_request) Has been cancelled
Build & Test (Linux) / ascend-test (pull_request) Has been cancelled
Build & Test (Linux) / integration-test (pull_request) Has been cancelled
Build & Test (Linux) / CI Gate (pull_request) Has been cancelled
Qoder Auto Code Review / qoder-review (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: mooncake-track/Mooncake#3
No description provided.