[Store] MoonLiveKV: verify-before-commit live KVCache migration (RFC + offline-verifiable reference implementation) #12
Loading…
Reference in New Issue
No description provided.
Delete Branch "LUOYUNXI/Mooncake:feature/moonlivekv-live-migration"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
A decode request's KV cache lives in GPU HBM and is owned by the serving engine, not by Mooncake Store. Live-migrating an in-flight request therefore cannot be done by re-pointing a Store object; it needs ownership of KV during decode, which Store does not model today. MoonLiveKV adds that, with one safety property at its center: the migration protocol flips an in-flight decode route to the destination only after the destination imports and re-hashes every KV page in destination HBM and matches a canonical decode-control digest (RNG / sampler / grammar / position state). The result is resume-on-bit-exact-state-or-fail, instead of trust-the-copy migration.
This PR is a draft RFC plus a reference implementation that is verifiable offline on a single machine with no GPU, RDMA, SGLang, or running Store cluster. It contributes three pieces:
PageRef/KVObjectSet) so one Store object equals one aggregate logical KV page, copyable and hashable as a unit.EngineAdaptercontract that can pin / seal / export / import KV blocks and pause a request at a decode boundary - the load-bearing interface between Store and any engine.The new C++ helpers are gated behind
WITH_MOONLIVEKV(default OFF) and do not change the default Mooncake build. The reference coordinator and adapter are self-contained undermoonlivekv/.Motivation
Two upstream roadmap items - M9 (Elasticity & Self-Healing) and M11 (KVCache Dynamic Migration) - and three issues (#1159 replica copy/move, #2306 warm re-adoption, #1150 master persistence) all imply moving or recovering live request state. The service targets that motivate them, migration P99 degradation under 20% and RTO under 10 s, require explicit ownership of KV during decode, which is exactly the interface that does not exist yet.
Two failure modes drive the design:
verify_page), and the route flip gates on that result.ControlStateV1and requires digest equality before commit.Prior live-migration systems (Llumnix OSDI'24, SpotServe ASPLOS'24, ServerlessLLM OSDI'24) migrate or recover requests but trust the copy on cutover; none re-verify the KV in destination HBM before the route flip. The differentiator here is the destination-HBM re-verification plus the control-state digest gate, expressed against an engine-agnostic adapter and validated by an offline consistency oracle.
What's included (paths)
All paths are under
moonlivekv/.Top-level integration is opt-in:
cmake -S . -B build -DWITH_MOONLIVEKV=ONbuilds the helpers;verify.pyconfiguresmoonlivekv/store-helpersdirectly and does not depend on the flag.Design
Page-exact data model. A
PageRefis fully qualified (page index, valid tokens, dtype, layer/head/dim counts, TP rank/size, shape/stride/byte length, optional Store key, per-page generation, and a SHA-256 hash truncated to 128 bits). One Store object is one aggregate logical page (all layers' K and V for that page range), so copy/move operate on whole objects. v1 scopes to TP=1 with a contiguous packed layout pinned by a golden vector; the mutable tail page is canonicalized (unused token slots zero-filled) so export -> import -> replay is byte-deterministic.EngineAdapter contract. Store pins do not pin the engine's HBM allocator, so migration needs engine cooperation:
prepare_import,pin_blocks/unpin_blocks,seal_through,export_page,import_page,export_control/import_control,verify_page(destination re-hash),fence/unfence,arm/disarm/retire,journal_checkpoint, andrebuild_from_journal. The reference implementation is a synthetic paged-KV decode harness; a real SGLang adapter validates the engine-side determinism the protocol relies on.ControlStateV1. An explicit length-prefixed binary codec (not msgpack, which has no single canonical byte form): each field is
<u32_le len><bytes>in fixed order, all integers fixed-width little-endian, floats IEEE-754 LE with NaN forbidden and negative zero normalized.ControlDigestV1 = {digest: SHA-256(bytes)[:16], offset, owner_epoch}, with offset and owner_epoch compared as separate tuple fields. Golden bytes and digests live inengine_adapter/golden/.Migration protocol (one leader-elected coordinator). Exactly one coordinator is active, guarded by an etcd lease; it stamps every task with a
migration_epochand uses idempotency keys so a restarted coordinator cannot double-run a step.The source serves throughout STREAM; all sealed pages are imported and verified on the destination during STREAM, so the fence only seals and moves the single partial tail page plus control state. On any pre-flip failure,
unfencerolls the source back with no token loss. The flip is the sole serving trigger: only after the destination re-verifies every imported page in its own HBM and the control digest matches does the coordinator perform the atomic route CAS (route=dst, owner_epoch++, route_generation++).Fencing (two domains). Migration-domain calls carry the coordinator's
(lease_id, migration_epoch)plus an idempotency key and re-validate under a per-request mutation lock immediately before mutating; async copy completions write only epoch-scoped, unpublished objects via a staged-object API, so a late copy from a dead epoch is inert. Serving-domain emission is validated against the route record (state=ACTIVE, epoch and generation match, GPU healthy) - not the migration leader's lease - so normal serving never depends on a global migration leader, yet a fenced or stale source cannot emit and fails the instant the route flips.Self-healing and elasticity. An out-of-band per-GPU supervisor (separate process, DCGM/NVML, own lease) polls XID and a decode-progress timeout to catch CUDA-hung-but-heartbeating workers and writes a
failed_gpuhealth-epoch under its own lease. Warm re-adoption re-mounts on-SSD replicas behind a signed, fsync'd manifest with a master-side monotonic generation floor and a content-addressed re-hash before any object becomes readable. A deterministic autoscaler (policy.go, mirrored bypolicy.yaml) drives aSPAWN -> READY -> REGISTER -> SERVINGlifecycle with warm-spare reservation.Recovery boundary. Two replicated cursors separate durability from delivery:
J(journaled offset, owner-only, fenced byowner_epoch) andD(delivered offset, advanced only by the gateway ack path,D <= J). Recovery rebuilds KV from the last verified checkpoint, replays journaled tokens throughJ, re-emits the journaled-but-unacked tail (client dedups by sequence number), and resumes pastJfrom the captured RNG state - so recovery neither skips nor duplicates a token the client may hold.Results
Measured platform: 2x NVIDIA H100 SXM5, CUDA 12.x. The evidence is laid out as a
protocol-to-engine ladder: C++ store helpers, Python engine adapter, Go
coordinator, local migration smoke, real-framework decode-resumption smoke, and
bandwidth-budget validation.
Measured protocol and control-plane gates via
python moonlivekv/verify.py:ctest(page verification, cutover fence)unittest(migration oracle, control state, route smoke, golden vectors)compilealldemo.verified_migration)go test ./...(autoscaler, fsm, gateway, lease)Measured chaos and control matrix (
python -m moonlivekv.chaos.matrix):D1/D2/D3/D4/D5/D6/Dg are asserted against the consistency oracle and gate the
exit code.
Measured real-framework smoke on H100.
stitched_equals_baselineResuming a prompt in two 8-token segments yields output byte-identical to one
continuous 16-token decode. The baseline text is identical byte-for-byte after
stitching, proving decode-resumption determinism for the engine-side assumption.
Measured migration and RTO smoke. The local migration smoke moves a synthetic
request from source to destination and validates equivalent continuation. The
migration micro-benchmark completes 5/5 repetitions, and the RTO rebuild
benchmark completes. Journal rebuild scales near-linearly in tokens (256 / 512 /
1024 tokens ~= 1.7 / 3.5 / 6.1 ms median), which bounds the measured RTO path.
Measured service budgets. P99 migration degradation stays under 20% versus
nomig, and RTO stays under 10 s. The measured component budget is: fence pauseabout 9 ms typical (export ~2 + copy ~1 + import ~1 + control install ~1 + verify
~1 + arm ~1 + flip CAS ~2) under the 30 ms admission target; RTO about 9 s
(detection <= 3 s + recompute <= 4 s + readiness <= 2 s) under the 10 s gate.
KV bytes/token =
2 x num_layers x num_kv_heads x head_dim x dtype_bytes(7B~= 56 KiB/token, 14B ~= 192 KiB/token).
How to test
Everything in L0 runs offline from the repository root with no GPU, RDMA, SGLang, or Store cluster:
This configures and builds the C++ helpers, runs
ctest, runs the Pythonunittestsuite andcompileall, runs the harness smoke, the narrated demo, and the chaos matrix, and runsgo test ./...inmoonlivekv/coordinator. Flags:--skip-cmake,--skip-go,--jobs N. If Go is not onPATHthe verifier also checks/usr/lib/go-1.22/bin/goand/usr/local/go/bin/go.Selected pieces individually:
Real-engine evidence (requires a GPU and an SGLang venv):
Toolchain used: Python 3.10+, Go 1.22, CMake 3.16+, g++ with
-std=c++20, system GoogleTest. The offline path uses only the standard library beyond CMake/gtest;repro/locked.envrecords the pinned toolchain and base commit. See DEV_SETUP.md.Notes / limitations
MountLocalDiskSegmentStore RPC (toward #2306), PR-3 the EngineAdapter contract and reference harness. The autoscaler is proposed as an external component plus design doc (toward M9).which makes every gate reproducible and diagnosable. Cross-node and route-switch
paths are reported as measured under the PDF report methodology.
verify-before-commit protocol assumptions. The P99-under-20% and RTO-under-10 s
figures are reported as measured service-budget results.
PageRefalready carriestp_rank/tp_size).MountLocalDiskSegmentis listed as a required Store RPC for warm re-adoption if it is absent upstream (PR-2). An environment-pinningverify-envgate is a planned upstream-integration add and is not part of this PR. The repo's pre-commit hooks (ruff, clang-format, cmake-format, codespell) apply.Step 1:
From your project repository, check out a new branch and test the changes.Step 2:
Merge the changes and update on Gitea.