Skip to content

[Bugfix][Store] Support Dummy external buffer registration - #1

Open
zxpdemonio wants to merge 996 commits into
mainfrom
codex/dummy-register-buffer-pr
Open

[Bugfix][Store] Support Dummy external buffer registration#1
zxpdemonio wants to merge 996 commits into
mainfrom
codex/dummy-register-buffer-pr

Conversation

@zxpdemonio

Copy link
Copy Markdown
Owner

Description

DummyClient previously accepted only pointers inside its own shared-memory
segments. As a result, register_buffer(ptr, size) rejected ordinary host,
pinned-host, and CUDA allocations, and external addresses could not be used
safely by the Dummy read/write APIs.

This PR adds explicit external-buffer registration semantics:

  • register host, pinned-host, and CUDA addresses outside Dummy SHM;
  • reference-count repeated registration of the exact (base, size);
  • reject same-base size changes and partially overlapping ranges;
  • validate address/size and hugepage-alignment overflow;
  • require exact base addresses for unregister and erase on the final release;
  • clear external registrations during teardown;
  • validate every external transfer range before staging;
  • retain CUDA IPC fast paths and use owned SHM staging only where the Dummy
    RPC boundary requires it.

The change is limited to DummyClient registration and generic external
pointer transfers. Tensor-specific *_from multi-buffer routing is covered
by PR-2.

Module

  • Transfer Engine (mooncake-transfer-engine)
  • Mooncake Store (mooncake-store)
  • Reshard (mooncake-reshard)
  • Mooncake EP (mooncake-ep)
  • Integration (mooncake-integration)
  • Python Wheel (mooncake-wheel)
  • Common (mooncake-common)
  • CI/CD
  • Docs
  • Other

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Breaking change
  • Documentation update
  • Performance improvement
  • Other

How Has This Been Tested?

Test commands:

cmake --build /root/mcake-pr1-build --target dummy_client_get_buffer_test -j16
ctest --test-dir /root/mcake-pr1-build -R dummy_client_get_buffer_test --output-on-failure

Test results:

  • CUDA-enabled build passed.
  • dummy_client_get_buffer_test passed: 15 tests, latest run 77.5 s.
  • External host registration lifecycle covered: duplicate registration,
    size mismatch, overlap rejection, overflow rejection, partial transfer, and
    exact unregister.
  • Repeated CUDA/non-CUDA targeted runs passed on machine 70.
  • Full Python suite — not required for this focused C++ registration PR.

Review notes:

  • Production/test diff is 225 changed lines.
  • register_buffer itself performs registration bookkeeping only; it does not
    allocate a staging buffer or copy user data.
  • External Dummy transfers may use one operation-scoped SHM staging allocation
    because the RPC handler runs against the Dummy-mapped address space.

Aionw and others added 30 commits July 31, 2026 18:05
…-ai#3235)

Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.3 to 1.82.1.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](grpc/grpc-go@v1.79.3...v1.82.1)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.82.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…cache-ai#3171) (kvcache-ai#3184)

* [ROCm] Add ROCm/HIP wheel build, CI, and release parity with CUDA

Closes the ROCm CI parity gap (kvcache-ai#3171). Mooncake already supports AMD GPUs
in the transfer engine (USE_HIP, hip_transport, dmabuf MR registration from
kvcache-ai#2225), but there was no CI to build/test the wheel and no published
mooncake-transfer-engine-rocm package.

The transfer engine is torch-independent and has no device kernels, so the
ROCm wheel is GPU-arch-agnostic (one wheel covers gfx942 and gfx950) and the
ROCm runtime (libamdhip64/libhsa-runtime64) is auditwheel-excluded and bound
at load time -- so the same wheel works in both the upstream
vllm/vllm-openai-rocm and lmsysorg/sglang ROCm images.

- scripts/build_wheel.sh: add HIP_BUILD variant -> mooncake-transfer-engine-rocm
  (ROCm runtime libs were already in the auditwheel exclude list).
- .github/workflows/ci_rocm.yml: PR build check in the ROCm dev image
  (py3.10 + py3.12), mirroring ci_cu13.yml. No GPU needed to compile.
- .github/workflows/release-rocm.yaml: tag-triggered build+publish, mirroring
  release-musa.yaml (self-contained container build), py3.10-3.13.
- ci.yml: wire build-wheel-rocm into the CI graph and CI Gate.

Validated on real hardware (build in rocm/dev-ubuntu-22.04:7.2.3-complete,
installed into the target images):
- MI300X gfx942 (Hotaisle): SGLang v0.5.16-rocm720-mi30x (cp310) and
  vLLM v0.26.0 (cp312) -- mooncake_master --version + tests/test_transfer_on_hip.py
  (2/2 sync read+write loopback transfers) pass on-device.
- MI355X gfx950 (Crusoe): same images (mi35x variants), same tests.

Signed-off-by: andyluo7 <andy.luo@amd.com>
…ache-ai#3210)

* [TE] perf(efa): allow bounding the batch MR registration fan-out

registerLocalMemoryBatch() and unregisterLocalMemoryBatch() spawn one
std::async(std::launch::async) per buffer with no cap, which libstdc++
takes literally -- one fresh thread each. Kimi-K3 registers ~180 KV
buffers per TP rank in a single batch, and with one TransferEngine per
rank an 8-rank node peaks at ~1100 threads all inside fi_mr_reg /
fi_mr_regattr.

Replace both fan-outs with a fixed pool pulling from a shared index,
sized by MC_MAX_CONCURRENT_REG_MR. The default of 0 means unbounded, so
behavior is unchanged unless an operator sets the knob: with no cap the
pool spawns count-1 threads and runs the caller as a worker, matching
what std::async did. Error semantics are also unchanged -- every item is
still attempted and the first non-zero return is propagated.

The knob is opt-in with no built-in default because its effect depends on
the order the caller passes buffers in, which this layer cannot see.
Measured on p5.48xlarge (32 NICs) replaying K3's registration the way
SGLang issues it -- 8 processes, one engine and one GPU each, 182 buffers
of 2.5 KB to 391 MB per process, barrier-synchronized. Slowest rank,
since nothing serves until all 8 finish:

  per-proc cap   descending      pool order (SGLang's)   ascending
  unset          108/99/128 s    112 s                   120 s
  64             184/208 s       101 s                    60 s
  16              95/98/97 s      43 s                    36/37 s
   8             107 s             --                      44 s
   4             157 s             --                      --

A cap of 16 is worth 2.6x on the order SGLang actually uses, but only
1.1x on descending order, and the order alone swings the capped result by
2.7x. Unbounded is order-insensitive (99-128 s) because nothing queues.
Largest-first being the worst order contradicts longest-processing-time
scheduling and is still unexplained, so no sort is applied here yet.

Two other platforms put the optimum elsewhere, which is the other reason
not to compile in a default: p5 host memory (fi_mr_reg), 128 x 2 GiB of
4 KB pages, prefers cap 16 (59.1 s vs 274 s unbounded); a 2x p6-b300 K3
server run at 16 NICs/rank prefers cap 8 (20.7 s vs 138.7 s unbounded).
A core-scaled default would be worse still on a large node -- where
capping helps, the bottleneck is the provider lock, not CPU.

So the code keeps its historical behavior and only exposes the pool size.

* [Doc] condense the MC_MAX_CONCURRENT_REG_MR entry

The bullet had grown into a multi-paragraph section with measurement
tables, out of proportion to every other entry in this list. Keep the
operationally relevant facts -- the cap is per process, the default is
unbounded, a good value is platform- and order-dependent, do not
core-scale it -- and leave the supporting measurements to the PR.

* [TE] docs(efa): correct the MC_MAX_CONCURRENT_REG_MR guidance

The comment and docs claimed the cap should not be core-scaled, on the grounds
that the bottleneck was a shared provider lock rather than CPU. Both halves were
wrong, and both came from a single-process benchmark where 48 registration
threads on a 192-core node never contended.

Measuring at the real topology instead -- one TransferEngine per TP rank, and
varying both the rank count and the core budget -- shows the optimum is set by
the CPU budget: 8 ranks/192 cores prefers cap 16, 4 ranks/192 cores prefers 32,
and 8 ranks/64 cores prefers 8. All three are the same ~cores global thread
count, so a good value is roughly cores/processes-per-node. The lock hypothesis
is refuted separately by a 23x speedup from capping a serial baseline, which a
size-proportional global lock cannot produce.

Also drops the retracted single-process figures and shortens the comment.

No functional change.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…g the same batch key, lead to OBJECT_ALREADY_EXISTS/persist failed/INVALID_KEY (kvcache-ai#2967)
…ions (kvcache-ai#3230)

Co-authored-by: maxlisongsong <maxlisongsong@didiglobal.com>
…i#3236)

Co-authored-by: maxlisongsong <maxlisongsong@didiglobal.com>
…3066) (kvcache-ai#3073)

Two bugs in the BatchLoad io_uring path could cause short reads to
pass validation silently:

1. storage_backend.cpp: read_res was overwritten with the expected
   value before the downstream short-read check, making it dead code.

2. uring_file.cpp: collect() used io_uring_for_each_cqe which
   consumes all CQEs without user_data filtering, allowing stale
   CQEs from previous failed operations to contaminate byte counts.

Fix: add min_required validation before pointer adjustment, and
filter CQEs by monotonic op_id via sqe->user_data with peek-then-wait
to avoid extra syscalls on the common path.

Closes kvcache-ai#3066.

Co-authored-by: Claude <noreply@anthropic.com>
…e-ai#3242)

isGpuDirectRdmaSupported only recognized nvidia_peermem, so ROCm hosts never
enabled gpu_to_gpu and VRAM RDMA transfers were rejected at transport select.

Signed-off-by: staryxchen <staryxchen@tencent.com>
…ai#3248)

Hardcoding "cuda:" skipped hip:N topology entries, so MC_ENABLE_HCA_PEER_AFFINITY never applied on ROCm builds.

Signed-off-by: staryxchen <staryxchen@tencent.com>
…e-ai#3246)

Remote reads of SSD-offloaded objects failed with '[RDMA] No rkey for
MR access' because FileStorage::RegisterLocalMemory registered the
client buffer pool (which backs offload read results) with
remote_accessible=false.  With remote_accessible=false the pool's
BufferDesc is published with an empty rkey array, so any peer that
RDMA READs the pool hits the rkey-range check in selectPeerDevice.

Fix: register with remote_accessible=true, matching how DRAM segments
are registered in MountSegmentAndGetId.  No performance impact (MR
access flags do not affect transfer rate; registration is a one-time
startup cost).

Signed-off-by: tan changzhi <544463199@qq.com>
Co-authored-by: Yuchen Kou <kouyuchen@approaching.ai>
* [Bugfix][TE] Fix dma-buf offsets for chunked GPU MRs

Fix dma-buf offset calculation for chunked GPU memory regions by incorporating each chunk's relative displacement, and add a GPU loopback regression test.

Signed-off-by: Ruiqing Feng <ruiqing.feng@broadcom.com>

* [CI] Suppress libnuma.so LSan false positive in async event drain test

Suppress LSan false positive from libnuma.so's static topology cache in
rdma_async_event_drain_test, fixing process teardown exit code 8 in CI.

Signed-off-by: Ruiqing Feng <ruiqing.feng@broadcom.com>

---------

Signed-off-by: Ruiqing Feng <ruiqing.feng@broadcom.com>
…kvcache-ai#3196)

* add option for USE_VRAM_SEGMENT

---------

Co-authored-by: 沈冲 <hz_yongke.zyk@antgroup.com>
…ai#3268)

* Restrict nightly workflow to upstream repository

* Simplify nightly repository guard
* [FEAT] add SUPA (Biren GPU) support to transfer engine

Introduce gpu_vendor/supa.h CUDA-alike shim mapping cuda* runtime
APIs to supa* equivalents. Wire USE_SUPA option through common.cmake
(SUPA_ROOT / driver lib discovery) and transfer-engine CMakeLists.
Adapt rdma_context, rdma_transport, transfer_engine_bench, and
cuda_alike.h to build against SUPA runtime/driver when USE_SUPA is ON.

* [TEST] enable GPU mem backend under USE_SUPA in rdma_transport_test

rdma_transport_test.cpp's GPU-memory paths (pickBackend/pickDevId/validateBackend/
setBackendDevice/allocateMemoryPool/freeMemoryPool/copyFromHost/copyToHost and the
checkCudaError helper) were guarded by USE_CUDA||USE_MUSA||USE_HIP||USE_MLU||
USE_MACA||USE_HYGON||USE_COREX but omitted USE_SUPA, so under the SUPA build the test
fell back to mem_backend=cpu and validateBackend("gpu") exited with EXIT_FAILURE --
the GPU-mem RDMA case could not run.

Add USE_SUPA to each of those guards. cuda_alike.h already #includes gpu_vendor/supa.h
under USE_SUPA, which aliases cudaMalloc/cudaFree/cudaMemcpy/cudaSetDevice/
cudaError_t/cudaSuccess/cudaMemcpyHostToDevice/DeviceToHost to the SUPA runtime, so
no new call sites are needed. USE_CUDA branches are untouched.

Verified on FPGA 15.1+15.2 (BR208x2, mlx5_0, peermem=false): 15.2 target +
15.1 initiator with --mem_backend=gpu --protocol=rdma, shared etcd 6.6.6.2:2379.
initiator: 'Remote buffer location: supa:0' + 'RDMA compare: OK' rc=0;
target: 'Received RDMA ready ACK'. The SUPA alias path (supaMalloc + dmabuf MR
export via exportDmabuf/registerMemoryRegionInternal) is exercised in-test.

* [BUILD] resolve BIREN_HOME first in common.cmake USE_SUPA block

Replace envSUPA_PATH/SUPA_ROOT + SUPA_DRIVER_LIB_DIR two-var lookup with a
single BIREN_HOME-first lookup (env BIREN_HOME -> /usr/local/birensupa/all/latest),
then include ${BIREN_HOME}/supa/include and link ${BIREN_HOME}/supa/lib
${BIREN_HOME}/brumd/lib. message BIREN_HOME so the resolved root is visible
at configure time.

src/CMakeLists.txt unchanged (target_link_libraries supa supart via dirs above).

Verified on FPGA 15.2: cmake reconfigure prints 'BIREN_HOME: /home/ubuntu/fullstack/full-stack';
transfer_engine + rdma_transport_test build clean; ldd (envs sourced) resolves
libsupa.so.2 / libsupart.so.2 / libbebr.so.2 from brumd/lib + supa/lib.

* [CHORE] remove stale branch-A note from supa.h alias block

* [STYLE] clang-format/cmake-format only the SUPA MR-touched lines

Apply clang-format-20 and cmake-format to the lines this SUPA
transfer-engine MR actually added/modified; no whole-file reflow of
pre-existing style debt on adjacent code.

- supa.h (new file): collapse aligned #define macro columns to
  single-space per repo .clang-format
- cuda_alike.h: pad the #if continuation line aligned with the
  USE_SUPA clause added by this MR
- rdma_context.cpp: wrap the #if line that grew past ColumnLimit 80
  after appending USE_SUPA
- rdma_transport_test.cpp: pad #if/#elif continuation lines aligned
  with the USE_SUPA clause added by this MR
- common.cmake: reflow the set(... CACHE) calls inside the MR-added
  USE_SUPA BIREN_HOME block

Verified in-place:
- clang-format-20 --dry-run --Werror: PASS on all 6 MR-touched cpp/h
- cmake-format --check: the MR-added CMake hunks are clean; remaining
  cmake-format findings on these files are pre-existing style debt on
  lines this MR did not touch and are intentionally left unchanged.

---------

Co-authored-by: e00977 <yqcai@birentech.com>
…nit (kvcache-ai#3263)

Co-authored-by: maxlisongsong <maxlisongsong@didiglobal.com>
…cal NICs (kvcache-ai#3219)

The EFA transport registers every single-chunk buffer on every NIC. That is
incidental to the `chunks.size() <= 1` branch rather than deliberate: the
multi-chunk paths already partition NICs, only the common case does not.

For device memory the cost is not linear in the NIC count but worse. EFA
charges a device-memory registration roughly in proportion to the device
bytes already registered on that libfabric domain, and that cost is paid
once per domain, so fan-out multiplies accumulation. Measured on
p5.48xlarge (32 NICs, 8 GPUs), 48 x 391 MB GPU buffers registered serially:

    all NICs (32)          116.1 s
    topology-local (4)      14.9 s   -- 7.8x

An 8-rank replay of a Kimi-K3 KV registration (182 GPU buffers per rank,
14.3 GiB, one TransferEngine per rank as SGLang does it) at
MC_MAX_CONCURRENT_REG_MR=16, in the buffer order SGLang actually passes:

    all NICs (32)   35.0 s wall   2981 CPU-s   85 cores busy
    topology-local  5.2 s wall     394 CPU-s   77 cores busy   -- 6.7x / 7.6x

This does not subsume MC_MAX_CONCURRENT_REG_MR. `local` cuts the work per
registration, not the number of them in flight: the thread count is the
buffer count, independent of the NIC set. Unbounded, a `local` batch still
peaked at 1289 threads and 182 of 192 cores busy, versus 168 threads and 77
cores with the cap. The two are orthogonal and both are needed.

Add MC_EFA_NIC_SELECTION=all|local. `local` narrows a single-chunk *device*
buffer to the NICs the topology reports as closest to its GPU.

No new PCI walk or hwloc dependency: Topology::discover() already computes
exactly this. For a "cuda:N" entry, preferred_hca holds the HCAs at minimum
PCI distance from that GPU within its NUMA node -- on p5.48xlarge the 4 EFA
devices sharing the GPU's PCIe root complex, the same rail group NIXL's
libfabric plugin derives from hwloc. buildLocalNicMap() just inverts that
into context_list_ indices. Verified on hardware: each of the 8 GPUs
resolves to its own 4 rails, and the timing above is symmetric across GPUs
(14.9 s on GPU 0, 15.1 s on GPU 5), so it is per-GPU rather than always
GPU 0's set.

Partial registration needed no new plumbing: lkey/rkey are already pushed
as 0 for unassigned NICs, and selectDevice() already retries past a NIC
whose rkey is 0.

Host memory is deliberately excluded. It has no per-domain accumulation to
amplify, it is ~4x cheaper to register anyway, and a host buffer has no
single owning device -- "cpu:N"'s preferred set is a NUMA node's NICs,
measured as 16 of 32 here, half the machine rather than a rail group.

Opt-in, defaulting to the historical behavior, because it trades away
per-buffer bandwidth: fewer NICs can serve a transfer touching that buffer.
The RDMA transport registers on every context unconditionally too, so
defaulting `local` on would also put the EFA transport out of step with the
rest of the codebase.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…vcache-ai#3265)

Co-authored-by: maxlisongsong <maxlisongsong@didiglobal.com>
waizuichougou and others added 10 commits August 26, 2026 20:04
…truction (kvcache-ai#3584)

* [TransferEngine] Fence stale RDMA handshakes after QP reconstruction

Signed-off-by: He Zhou <zh541072@alibaba-inc.com>

* [TransferEngine] Add stale RDMA handshake regression test

Signed-off-by: He Zhou <zh541072@alibaba-inc.com>

---------

Signed-off-by: He Zhou <zh541072@alibaba-inc.com>
Co-authored-by: He Zhou <zh541072@alibaba-inc.com>
…vcache-ai#3684)

An idle pooled connection only learns that its peer restarted when it is
next used, and that first use fails. Discarding just that one client left
every other connection pooled for the same peer equally stale, so the
caller paid one failed call per stale connection. TcpTransport retries a
bounded number of times (max_retry_count), so a peer that was already
back up could still fail a transfer outright, and notifications, which
have no retry above this layer, were dropped.

Drop the whole pool for that address instead, so the next attempt starts
from a fresh connection.

This deliberately does not retry the failed call in place. Pin, Unpin,
Delegate and Notify share this path and mutate remote state, and the
error tells us nothing about whether the peer ran the handler: ylt
reports both "never sent" and "ran, but the reply was lost" as io_error.
Recovery stays with the layer that already owns it and knows whether its
operation is safe to repeat.

The flush is skipped when the failure came back from the peer as a reply
- a handler exception, an unknown function id, a rejected argument. The
connection carried a full round trip in those cases, so it is fine, and
so are the ones other callers of the same address are holding.

The pool also carries a generation. A client is handed out with the
generation it came from, and a failure only flushes if that is still the
current one. Several callers notice the same restart at once; without
this the second one to fail would throw away the connections the first
one has already re-established, and the pool would churn for as long as
stale clients keep surfacing.

rpc_reconnect_test:

- StalePoolIsFlushedSoTheNextCallSucceeds - a concurrent burst parks two
  connections in the pool, the peer restarts on the same port, and the
  call after the first (unavoidable) failure has to succeed. Fails
  without the flush.
- AFailedCallIsNotRetried - the peer runs a side effect and only then
  fails; it must have run exactly once. Fails against a version that
  retries, reporting two executions.
- HandlerFailureLeavesThePoolAlone, DeadPeerStillFails,
  ConcurrentStaleCallsAllRecover - guards for behaviour that must not
  change. They pass either way and say so in their comments.

tent suite: 31/31 passed.

Co-authored-by: Frank <xiaodouzi6661@gmail.com>
…e-ai#3562)

* [TransferEngine] Reuse TCP CUDA staging buffers across chunks

Cache accelerator device detection once per request and reuse a growable staging buffer within TCP client and server sessions instead of allocating one for every chunk.

Add CUDA regression coverage for multi-chunk READ/WRITE correctness and staging allocation/device-query counts.

Refs kvcache-ai#3446

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix ci problem

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
batchTransferSync returned -1 silently from three paths: openSegment
failure, submitTransfer failure, and exhausting all retries. A failed
KV/state transfer therefore surfaced to callers (e.g. sglang) as an
opaque -1 with nothing in the engine log, which is what made the
NVLink ordinal bug look like a mystery. Log each path with the target
host, batch size, total length and retry context.

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

* fix(rdma): evict stale endpoints on port recovery (fixes kvcache-ai#3596)

When an RDMA port goes down and comes back up, the IBV_EVENT_PORT_ACTIVE
path only called context->resume(), flipping a DeviceStatus flag.  QPs
that entered IBV_QPS_ERR while the link was down were never destroyed or
rebuilt.  The endpoint store kept returning these stale endpoints, so
work requests were posted into dead QPs and never completed.

Fix: add EndpointStore::evictAll() to both FIFOEndpointStore and
SIEVEEndpointStore.  evictAll() moves every entry from the active map
to the waiting list and calls beginDestroy() on each, transitioning QPs
to ERR state so the hardware flushes inflight WRs to the CQ.

RdmaContext::resume() now calls evictEndpoints() (which calls
evictAll()) after successfully flipping status back to DEVICE_ENABLED.
The next getOrInsert() call for each peer will create a fresh endpoint
with a new QP, restoring full throughput.

Fixes kvcache-ai#3596

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

* fix(rdma): apply clang-format to evictAll() in SIEVEEndpointStore

The for-loop body on a single line exceeded the 88-column limit
enforced by clang-format-20 in CI.  Split it into a braced body.

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

* fix(rdma): add missing evictAll() declaration in SIEVEEndpointStore

SIEVEEndpointStore::evictAll() was implemented in endpoint_store.cpp but
not declared in the header, leaving the class abstract and causing a
compile error in tent-ci.

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

* fix(rdma): add missing evictAll() declaration in FIFOEndpointStore

FIFOEndpointStore::evictAll() was implemented in endpoint_store.cpp but not
declared in the header, leaving the class abstract.

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

* ci: trigger re-run

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

* ci: trigger re-run

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

* fix(rdma): insert into waiting_list_ under endpoint_map_lock_ in evictAll

Publish evicted endpoints into waiting_list_ while holding the
endpoint_map_lock_ write lock, instead of after releasing it.
reclaim() iterates waiting_list_ under endpoint_map_lock_, so an
insert outside the lock was an unsynchronized read/write on the
unordered_set (alogfans review on kvcache-ai#3604). beginDestroy() still runs
after the lock is released since it may call back into reclaim().

* ci: re-trigger CI

* ci: re-trigger

* ci: re-trigger CI

---------

Co-authored-by: zhaoye <zhaoye@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: zhaoye <yzhao04@iquestlab.com>
…vcache-ai#3677)

Metric updates took a mutex-protected map lookup per call, and because
TENT labels come from small fixed enums, all updates for a given label
landed on the same shard mutex.

Add cached_metric.h with CachedDynamicCounter (resolve() hands out a
stable per-label atomic cell; incCached() is a relaxed fetch_add) and
CachedDynamicHistogram (bucket counters and the sum counter built from
cached cells, replacing ylt's dynamic histograms and the parallel
standalone sum counters). TentMetrics slots N=1 metrics by TransportType
and N=2 attempt metrics by (transport, operation), resolving cells
lazily with race-free publication; the rare failover counter keeps the
locked path. Prometheus/JSON output is unchanged. Add a concurrency test
asserting exact counts when multiple threads resolve and update
overlapping label cells.
…vcache-ai#3678)

NVLinkTransport::relocateSharedMemoryAddress called cudaSetDevice() on
the peer BufferDesc's device index before cudaIpcOpenMemHandle. That
ordinal is relative to the PEER's CUDA_VISIBLE_DEVICES: with
per-instance CUDA_VISIBLE_DEVICES (e.g. GPUs partitioned between
prefill/decode workers) it can be out of range locally or point at a
different physical GPU, so every transfer to the peer's higher-numbered
devices failed -- silently, since CHECK_CUDA returned without logging.

Open the handle on the caller's current device instead:
cudaIpcMemLazyEnablePeerAccess takes care of the mapping, and the copies
already run cross-device via P2P. Remove the same device switch from
MnnvlTransport (its driver calls are device-agnostic).

Also: CHECK_CUDA/CHECK_CU now LOG(ERROR) before returning;
NVLinkTransport::submitTransferTasks rolls back half-appended task
entries on synchronous failure (no I/O has started yet).

Test: tent_nvlink_transport_test exports a real IPC handle from a
forked child and registers it with an out-of-range ordinal. Not
registered in ctest (needs a real GPU; run manually on a GPU box).

Signed-off-by: staryxchen <staryxchen@tencent.com>
…he scenario DSL (kvcache-ai#3656)

* [Store] Migrate object lifecycle and tenancy MasterService tests to the scenario DSL

Consume the typed DSL from kvcache-ai#3510 for the client-visible put, upsert,
revoke, batch, remove, and regex flows plus object groups, tenant
isolation, and quota admission listed in kvcache-ai#3572. Replace lease-expiry
sleeps with deterministic ExpireAt. Keep implementation-only assertions
as focused tests; SoftPinRequestValidation is trimmed to its internal
zero-TTL remainder.

Closes kvcache-ai#3572

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

* [Store] Restore lease-extension, combined-regex, and tenant key-count coverage

Address review on kvcache-ai#3656: three coverage regressions in the migrated
tests.

- RemoveLeasedObject: add the deterministic ExpireAt -> KeyExists ->
  Remove(OBJECT_HAS_LEASE) -> ExpireAt sequence so ExistKey extending an
  existing (expired) lease is asserted, mirroring the GetReplicaList
  half of the scenario.
- RemoveByRegexComplex: migrate the legacy /|\d$ case as a fifth
  scenario, asserting the five removed keys (two path keys, three
  trailing-digit keys) and the eight survivors exactly.
- TenantScopedPutsAndRemovesUpdateGlobalKeyCount: focused direct test in
  the monolith covering the legacy GetKeyCount 2 -> 1 cross-tenant
  transition; GetKeyCount stays outside the DSL vocabulary.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@zxpdemonio

Copy link
Copy Markdown
Owner Author

Opening the review against upstream kvcache-ai/Mooncake instead.

@zxpdemonio
zxpdemonio force-pushed the codex/dummy-register-buffer-pr branch from fba6f34 to 95fa49e Compare August 27, 2026 10:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.