Skip to content

Fix Mooncake HiCache registration for DeepSeek-V4 logical KV anchor - #26649

Open
Li-brua wants to merge 1 commit into
sgl-project:mainfrom
Li-brua:librua
Open

Li-brua wants to merge 1 commit into
sgl-project:mainfrom
Li-brua:librua

Conversation

@Li-brua

@Li-brua Li-brua commented May 29, 2026

Copy link
Copy Markdown
Contributor

Motivation

DeepSeek-V4-Flash uses a hybrid HiCache layout with a logical KV anchor pool. The anchor pool owns page indices but does not own a real KV buffer; the actual data is stored in hybrid side pools such as SWA, compressed KV, indexer, and state pools.
When enabling Mooncake as the HiCache storage backend, Mooncake previously assumed the primary host pool always had a physical kv_buffer and a Mooncake-supported memory layout. For DeepSeek-V4, this caused startup to fail during host pool registration because the logical anchor has kv_buffer=None and layout=layer_first.

Verify fix works. Launch server as follows:

Example Commands

CUDA_VISIBLE_DEVICES="0,1,2,3" \
MOONCAKE_MASTER="0.0.0.0:50058" \
MOONCAKE_DEVICE="mlx5_0,mlx5_1,mlx5_2,mlx5_3" \
MOONCAKE_PROTOCOL="rdma" \
MOONCAKE_GLOBAL_SEGMENT_SIZE="0" \
SGLANG_ENABLE_UNIFIED_RADIX_TREE=1 \
python -m sglang.launch_server \
    --served-model-name deepseek-v4-flash \
    --model-path /mnt/models/deepseek-ai/DeepSeek-V4-Flash \
    --host 0.0.0.0 \
    --port 30000 \
    --page-size 64 \
    --enable-hierarchical-cache \
    --hicache-ratio 1.7 \
    --hicache-mem-layout page_first \
    --hicache-io-backend kernel \
    --hicache-storage-backend mooncake \
    --hicache-write-policy write_through \
    --hicache-storage-prefetch-policy wait_complete \
    --trust-remote-code \
    --tensor-parallel-size 4 \
    --mem-fraction-static 0.8 \
    --enable-metrics \
    --enable-cache-report \
    --moe-runner-backend marlin \
    --max-running-requests 100 \
    --dist-init-addr "0.0.0.0:29500"

This PR fixes Mooncake storage registration for DeepSeek-V4 hybrid HiCache. #26647

Modifications

  • Detect logical KV anchor pools during Mooncake host pool registration.
  • Skip physical buffer registration and layout validation for logical KV anchors.
  • Use lightweight marker keys to track logical KV page existence in Mooncake.
  • Keep existing v2 registration and zero-copy I/O paths unchanged for real hybrid side pools.

Accuracy Tests

This change does not modify model forward logic, kernels, sampling, or numerical computation. No accuracy impact is expected.

Speed Tests and Profiling

This change only affects Mooncake HiCache storage registration and logical KV page existence tracking for DeepSeek-V4 hybrid HiCache.

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ✅ Run #27398986088
Latest PR Test (Extra): ❌ Run #27398985992

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@stmatengss stmatengss left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: 0 critical, 4 informational. Correct approach for skipping physical buffer registration on logical KV anchors.

@@ -597,6 +598,17 @@ def warmup(self):

def register_mem_pool_host(self, mem_pool_host: HostKVCache):
super().register_mem_pool_host(mem_pool_host)
self._logical_kv_anchor = (
getattr(self.mem_pool_host, "kv_buffer", None) is None
and getattr(self.mem_pool_host, "entries", None) is not None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] (8/10) Duck-typing heuristic: kv_buffer is None and entries is not None. Works for the current DeepSeek-V4 anchor shape, but if a future pool has kv_buffer=None for a different reason (e.g. lazy allocation), this would misclassify it. Consider checking an explicit attribute like is_logical_anchor on the pool if one exists, or adding one.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I replaced the kv_buffer/entries heuristic with an explicit is_logical_anchor marker on LogicalHostPool, propagated through HostPoolGroup.

marker = b"1"
for i, key in enumerate(key_strs):
if results[i]:
continue

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] (7/10) Sequential self.store.put() in a loop for each non-existing key. If many logical pages are set simultaneously, this is N individual RPCs to Mooncake instead of a batch write. Fine for small page counts; could become a bottleneck if the anchor pool is large. Consider batching if a batch-put API exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping this as-is for now. These are 1-byte marker values, while the available batch API here is the zero-copy batch_put_from(ptr, size) path for registered buffers. We can optimize if Mooncake exposes a batch bytes put API.

@@ -652,6 +683,10 @@ def _get_hybrid_page_component_keys(
suffixes = [f"{base_suffix}_temporal"] + [
f"{base_suffix}_conv_{i}" for i in range(conv_num)
]
elif name in getattr(self, "registered_pools", {}):
# DeepSeek-V4 and other hybrid side pools store one object per
# logical page. Use the pool name as the storage-key suffix.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] (7/10) getattr(self, "registered_pools", {}) silently returns {} if the attribute doesn't exist, making this branch dead code in that case. If registered_pools is guaranteed to be set by register_mem_host_pool_v2 before this method runs, consider using self.registered_pools directly so a missing attribute fails loudly rather than silently skipping the DeepSeek-V4 path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Changed this to use self.registered_pools directly so a missing initialization fails loudly.

@@ -843,6 +878,9 @@ def batch_get_v1(
# Apply extra_backend_tag prefix if available
keys = self._tag_keys(keys)

if getattr(self, "_logical_kv_anchor", False):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] (6/10) getattr(self, "_logical_kv_anchor", False) is used here, in batch_set_v1, and in batch_exists, but _logical_kv_anchor is always initialized in __init__. The defensive getattr is harmless but inconsistent with the direct self._logical_kv_anchor access in register_mem_pool_host. Pick one pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. _logical_kv_anchor is initialized in init, so I switched the call sites to direct self._logical_kv_anchor access.

@Li-brua

Li-brua commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Update after rebasing on latest main:

The latest main has already fixed the startup-side part of this issue by allowing Mooncake registration to skip physical KV buffer registration for logical/hybrid anchor pools, and it also includes more complete v2 handling for hybrid side pools.

This PR is still useful as a semantic follow-up: it makes logical KV anchors explicit via is_logical_anchor and tracks logical KV page existence with lightweight marker keys. This keeps the v1 batch_set / batch_get / batch_exists path consistent for logical anchors, instead of treating the logical KV anchor as always present and relying only on side-pool existence checks.

So the PR is no longer the sole startup fix, but it tightens Mooncake’s logical-anchor registration and existence-tracking semantics for DeepSeek-V4-style hybrid HiCache.

@stmatengss

stmatengss commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@stmatengss

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

Comment on lines +627 to +632
assert self.mem_pool_host.layout in [
"page_first",
"page_first_direct",
"page_head",
"page_first_kv_split",
], "mooncake store storage backend only support page first, page first direct, page head and page_first_kv_split layout"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should remove to parameter checking phases?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the extra layout assert from register_mem_pool_host. Backend/layout compatibility should stay in the argument/config normalization path, while this PR only needs to handle the logical-anchor
registration path.

Comment on lines +972 to +974
if self.mem_pool_host.kv_buffer is None:
# Non-KV logical anchors carry data through v2 side pools only.
return [True] * len(keys)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to change the position.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted the branch order so the non-logical kv_buffer=None fast path stays before key tagging. Logical KV anchors still fall through to the tagged marker-key path.

@Li-brua
Li-brua force-pushed the librua branch 2 times, most recently from 22d2b89 to abbeb27 Compare June 12, 2026 06:20
@stmatengss

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

@stmatengss

Copy link
Copy Markdown
Collaborator

/rerun-failed-ci

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation run-ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants