mirror of
https://github.com/kvcache-ai/Mooncake.git
synced 2026-11-05 01:17:55 +00:00
[GH-ISSUE #2849] [RFC]: TENT Receiver-Advertised Credits and Upstream Backpressure #738
Labels
No labels
ascend
auto-closed
bug
bug
CI/Build
Common
documentation
enhancement
feature request
good first issue
help wanted
high priority
MetaX
nightly-failure
pull-request
PyTorch Backend
question
RFC
Roadmap
stale
Store
Tests
Transfer Engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/Mooncake#738
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @catyans on GitHub (Jul 10, 2026).
Original GitHub issue: https://github.com/kvcache-ai/Mooncake/issues/2849
Changes proposed
Summary
This RFC proposes a receiver-advertised credit layer for TENT. It allows the node that will actually receive payload data to reserve and advertise bounded capacity before a sender dispatches work into a transport or remote staging path.
The proposal is a Phase 3 extension of #2132. It builds on the existing local runtime queue and dispatch window; it does not replace transport execution, RDMA congestion control, or the priority/byte-deficit work in #2655.
The key distinction is:
DispatchByteCredit: sender-local scheduler accounting, as discussed in #2655.ReceiverAdvertisedCredit: receiver-owned resource reservation shared across nodes, proposed here.The first implementation PR should add only the private credit state model, versioned message schema, and deterministic fault tests. It should not modify Admission scheduling or enable distributed credits by default.
This is the detailed follow-up for the receiver-credit part of #2132 Phase 3. It does not replace #2132's local queue design, and it deliberately does not duplicate #2655's sender-local dispatch byte credits.
Motivation
The current runtime queue can bound local outstanding owners/bytes and the amount of work handed to transports:
This is necessary but insufficient for many-to-one traffic. With 16 Prefill senders targeting one Decode receiver, every sender can independently observe local capacity while the receiver has exhausted one of these resources:
PFC/ECN/DCQCN protect the fabric but do not express application buffer or consumer readiness. One-sided RDMA WRITE also does not synchronize the initiator with the remote consumer. TENT therefore needs an explicit receiver-side resource contract before remote dispatch.
Goals
Expected Benefits and Claims Boundary
If the implementation satisfies the invariants and acceptance criteria in this RFC, it provides the following enforceable properties. These properties do not depend on an assumed throughput or latency improvement:
DATA_BYTES,REQUEST_SLOTS,STAGING_SLOTS, andCONSUMER_SLOTS. A resource that is not modeled or charged is not covered by this guarantee.TooManyRequests.This RFC does not claim a numerical P99/TTFT reduction, higher total throughput, better SLO attainment, or tenant fairness before the real-cluster matrix below is completed. Those are experimental outcomes, not protocol invariants. Tenant guarantees, caps, weights, borrowing, and authorization also require a separate QoS-contract/policing design. Healthy-path utilization must be measured against a correctly tuned fixed window, while overload tests must report receiver occupancy, stalls, failures, and data integrity in addition to latency and throughput.
Non-goals
Status::TooManyRequestswithout identifying the pressure source.Terminology and Direction
receivermeans the node whose memory/resource is receiving payload, not necessarily the API target:A plan that does not consume remote receiver resources must not be stalled by an unrelated remote credit ledger.
Design Principles
1. A grant is a reservation, not an estimate
The receiver may use telemetry to decide how much capacity to reserve, but advertised credit itself means the receiver has committed that resource to the peer/class. Unknown telemetry must not create credit.
2. Absolute cumulative grants within an epoch
Delta grants can be double-applied after retransmission or reordering. Instead, for every credit key the receiver advertises monotonically increasing cumulative totals:
The sender accepts an update only when:
session_idandepochmatch the active peer session;sequenceis newer than the last accepted update;Duplicate and old updates become no-ops.
3. Credits are non-revocable inside a live session
The receiver can stop granting more credit but cannot safely retract credit that the sender might already have consumed. Unused credit is returned explicitly or reclaimed after the peer session is fenced and old QPs/inflight operations are drained or failed.
The first version should keep initial grants small rather than implement unsafe timer-only reclamation.
4. Control traffic never consumes data credit
Credit update, usage, return, heartbeat, reset, cancel, and health messages use a bounded control budget. A zero data window must not prevent the message that reopens the window.
Credit Key and Resource Model
The receiver owns a global capacity allocator. Per-peer/per-class grants are carved from that allocator so that the sum of outstanding reservations cannot exceed configured physical capacity.
An Execution Plan produces a charge vector:
Examples:
DATA_BYTES + REQUEST_SLOTS + CONSUMER_SLOTS.STAGING_SLOTS.Proposed Wire Schema
The exact serialization can change, but the semantic fields must remain explicit and versioned:
Capability negotiation must happen during peer/bootstrap setup. The negotiated state is one of:
Unsupported: peer has no receiver-credit capability.OptionalLegacy: configured rollout allows legacy bounded local dispatch.Required: unsupported or stale credit blocks/fallbacks instead of assuming infinity.Active(version): typed updates are accepted.State Machine
Rules:
Active: new dispatch may reserve credit.Stale: stop new credit-gated dispatch; existing transport-owned work continues to terminal state.Dispatch Integration
Receiver credit belongs at dispatch eligibility, not queue admission:
The current
pickForDispatch()transitions selected owners to Dispatching. A production integration therefore needs one agreed boundary:requeueForDispatch()preserving original enqueue/aging time.This should be coordinated with #2655 rather than implemented in the first credit PR.
Local dispatch capacity and remote credit are both required:
Staging and Deadlock Avoidance
Credits should shape the physical inner transfers generated by ProxyManager, not only the outer user-visible staged request, matching #2132.
Staging-internal/control work needs reserved capacity that user payload cannot consume. Otherwise:
The resource allocator must distinguish user payload, staging-internal progress, and control messages. Bypass is allowed only when separately bounded and accounted.
Control Transport
TENT's existing notification QP is a useful prototype path because it is separate from data QPs and already has configurable SL/TC. It is not yet the protocol contract:
name/msgpayloads;The first wire PR may use a fake transport and ControlService for tests. Using the notification QP in production should be a separate reviewed step.
Control transport alternatives
BootstrapDescControlServiceRPCThe protocol model must remain independent of the selected transport. PR 1 and PR 2 should test through a fake bounded transport so that transport selection cannot weaken epoch, ordering or capacity invariants.
Backpressure API
TooManyRequestscurrently conflates queue admission, batch capacity, transport pressure, and staging exhaustion. Receiver-credit stall should expose structured information:Initially this can remain internal and observable through metrics. A later connector API can surface
BACKPRESSURE/RETRY_AFTERso SGLang/vLLM can pause submission, choose another Decode node, fallback, or recompute locally.Metrics
Per bounded peer/class labels:
Request IDs and unbounded tenant IDs belong in traces/sampling, not metric labels.
Compatibility and Rollout
Requestfield changes in the first PR.Proposed Code Placement and Concurrency Model
The exact file names are subject to maintainer feedback, but the ownership boundary should remain explicit:
CreditKey,CreditCharge, wire-neutral update typestent/include/tent/runtime/receiver_credit.hSenderCreditLedgertent/src/runtime/receiver_credit.cppReceiverCreditAllocatortent/src/runtime/receiver_credit.cpptent/include/tent/control/receiver_credit_protocol.hConcurrency rules:
Memory is bounded by configured limits, not request count:
Unknown peers/classes/resources, oversized updates and capacity multiplication overflow are rejected before allocation. Request IDs and tenant strings are not retained as unbounded ledger keys.
Security and Abuse Boundaries
Existing PG Mechanism: Cluster Evidence and Gaps
This RFC does not yet have a TENT implementation to benchmark. We therefore used Mooncake PG #1971 only as mechanism evidence, comparing upstream commit
4188e3awith the parent of #1971 (c3bafbed) on an 8×H20 host with four ~392 Gbit/s RDMA rails.wrong=0.64 MiB pool + 8 MiB chunkrun lost progress and hit a 150-second watchdog.sizeof(SegmentInfo); both sides timed out.These results support receiver-owned credits as a correctness direction, but they do not validate TENT throughput, SLO, or isolation. Before dispatch gating is enabled, the acceptance gate must include strict data validation across one and multiple rails, bounded pool liveness, and a versioned cross-node metadata handshake. Full details are recorded in
cluster-validation.mdbeside this draft.Implementation Plan
PR 1 — Credit model and protocol invariants
CreditKey,CreditLedger,CreditUpdateV1;PR 2 — Capability and control transport
PR 3 — Runtime dispatch gating
PR 4 — Staging and connector backpressure
Test and Experiment Matrix
Deterministic protocol tests
Real cluster experiments
Report:
Acceptance Criteria
Related Work
Open Questions for Maintainers
BootstrapDesc, add a ControlService RPC, or define a typed notification protocol?Before submitting a new issue...
@github-actions[bot] commented on GitHub (Jul 10, 2026):
Thanks for opening this issue, @catyans!
18214026A maintainer will triage this when possible. To help us respond faster, please include:
Useful links: Documentation · Contributing guide
@catyans commented on GitHub (Jul 11, 2026):
Final consolidated receiver-credit experiment report
This comment supersedes all earlier experiment comments on this RFC. The benchmark-only branch is
receiver-credit-experiment, at commit8e01c8d5. It does not modify production Admission or dispatch.What was corrected
The receiver JSON
p99_usstarts at a multi-slot lease demand and ends as individual slots complete. It is a lease-lifecycle / receiver-backlog metric, not a per-transfer end-to-end latency metric, so it must not be compared directly with fixed per-transfer demand latency.Commit
8e01c8d5moves the sender timer before credit acquisition. The sender P99 below therefore covers credit wait + request construction + RDMA transfer. Earlier interpretations of receiverp99_usas a per-transfer SLO metric are superseded.Direct capacity-safety evidence
On the backup RDMA hosts, sender-local fixed behavior exceeded receiver capacity, while receiver-owned credits enforced it:
The restored main cluster also reproduced the risk after reducing benchmark control polling from 50 us to the retained 10 us setting: one 16-sender fixed run reached 16/8 slots with 9 violations. Every corresponding credit run remained within capacity.
Final main-cluster 16-sender A/B
Five interleaved repetitions used 16 senders, an 8-slot receiver cap, 4 MiB transfers, lease batch 2, and the corrected sender end-to-end timer:
All credit runs completed all offered operations with zero data errors, zero capacity violations, zero invalid releases, and zero final occupancy.
Main-cluster throughput coverage
The broader three-repetition matrix produced these receiver-throughput medians:
Three integrity repetitions completed 12,288 4 MiB WRITE→READ pairs with zero data errors.
Parameter selection
The retained benchmark operating point is batch 2 with 10 us polling. Batch 2 is evidence for an operating point, not a proposed production constant; production should adapt grant size to capacity, active senders, and SLO pressure.
Verification and claims boundary
The focused C++ tests, Python result-verifier tests, JSONL mechanical checks, offered/completed equality, capacity guards, and integrity checks pass.
These results support the RFC's core claim: receiver-owned credits can enforce an aggregate resource bound without a material throughput, end-to-end P99, or fairness regression in the tested workload. They do not yet validate production restart fencing, replay/reorder handling, typed ControlService messages,
STAGING_SLOTS/CONSUMER_SLOTS, connector backpressure, or SGLang/vLLM TTFT improvements.@catyans commented on GitHub (Jul 11, 2026):
Draft benchmark PR opened: #2859
It contains the cleaned, benchmark-only
tebenchharness used to validate this RFC: receiver byte/slot capacity tracking, fixed-vs-credit demand/grant/release experiments, fixed-count integrity runs, corrected end-to-end sender timing, versioned JSONL, and mechanical verification.The PR deliberately excludes production Admission/dispatch integration and links back to this RFC for design discussion and experimental evidence.
@catyans commented on GitHub (Jul 11, 2026):
Draft PR1 is now open: #2860. It is intentionally limited to the private cumulative-grant ledger, versioned semantic types, epoch/replay fencing, bounded state, atomic multi-resource reserve/rollback, and deterministic protocol/concurrency tests. It does not add a control transport, capability negotiation, Admission/dispatch integration, background threads, or default behavior changes. Cluster validation on two independent H20 nodes includes 2000 repeated Release runs total, TSan, ASan/LeakSanitizer, and a CUDA-enabled TENT runtime build. I will keep PR2 transport-neutral behind a fake bounded control channel until the control-path choice in this RFC is aligned.
@catyans commented on GitHub (Jul 11, 2026):
PR1 #2860 is now Ready for Review with the full upstream matrix green and all modified coverable lines covered. The transport-neutral PR2 prototype has also passed two-node Release (1000 repeats per node), ASan/LSan, TSan, CUDA runtime build, bounded-inbox saturation, receiver restart fencing, and real cross-host mixed-version negotiation.
One production control-path decision is now blocking a clean PR2 interface:
CoroRpcAgenthandler does not expose caller identity).Both can reuse the validated capability, activation, epoch, bounded ingress, and peer-context models. Is push or pull preferred for TENT ControlService? I will keep the production RPC/default-off integration out of PR2 until this identity/control-flow choice is aligned, rather than baking a test-only constant into the interface.
@catyans commented on GitHub (Jul 11, 2026):
Implementation progress update (2026-07-12)
This comment records the current implementation boundary for the RFC. The core
credit invariants and a sender-side dispatch prototype now exist, but the RFC is
not yet implemented end to end. In particular, production control transport,
receiver allocation/telemetry, staged resources, typed backpressure, and
connectors remain follow-up work.
PR stack
PR3 is a single squashed delta (
8160d44) over the PR2 head (d2a3333). Theupstream GitHub view temporarily includes prerequisite commits because a PR
cannot use a fork-only PR2 branch as its base; after the prerequisites merge,
the visible diff will collapse to the single PR3 commit.
What is implemented now
receiver_credit/enabled=falseby defaultreceiver_credit/default_qos_classCurrent opt-in surface:
PR3 cluster validation
All validation was run remotely on the Lingjun H20 cluster, primarily on an
8 x H20-3e host (192 visible vCPUs, 2 TiB RAM, GCC 13.3, CMake 3.28.3,
NVIDIA driver 580.105.08). No workstation results are used as cluster evidence.
USE_CUDA=ON; 1.40 sKey deterministic observations:
Important remaining work
ControlServicepath with endpoint-bound identityDATA_BYTES + REQUEST_SLOTS; production gating must classify remote Direct WRITE vs local/READ and avoid charging unrelated pathsCONSUMER_SLOTSSTAGING_SLOTS/ ProxyManager inner workBackpressureInfoand metricsRevised next steps
ControlServicepush/pull flow, endpoint-boundidentity, bootstrap negotiation, and receiver global allocator.
and local SHM/NVLink under local accounting.
connected production path.
Explicitly not implemented
The following items are not present in the production path today. Passing
PR1/PR2/PR3 tests must not be interpreted as completing them:
ControlService.senders and QoS classes is at most physical receiver capacity.
committed cumulative grants.
fencing before old capacity is reused.
through the real control transport.
zero data credit.
operations; the prototype has not finalized Direct WRITE vs READ/local
accounting.
CONSUMER_SLOTScharging and release.STAGING_SLOTScharging and release.BackpressureInfowith exhausted resources, peer, QoS, retry hint,and fallback permission.
violation, fencing, and global-capacity production metrics.
recompute integration.
restart validation.
and dispatch wiring.
and data-integrity report.
RFC acceptance-criteria status
Current claims boundary
The implemented stack now validates the credit arithmetic, replay/restart
fencing, bounded sender-side dispatch decision, and exact rollback behavior. It
does not yet establish end-to-end receiver-capacity safety in production,
because real receiver capacity allocation and the production update transport
are not connected. PR3 also does not claim a new TCP/RDMA throughput or TTFT
gain; those measurements become meaningful only after that end-to-end path is
complete.
@catyans commented on GitHub (Jul 12, 2026):
Fresh feasibility rerun on 2026-07-13 against the stacked dispatch-gate head
8160d44a(feat/receiver-credit-dispatch-gate).Environment: Lingjun H20 node
lingjun-099, Linux 5.10.134, 2 x Intel Xeon Platinum 8575C (192 vCPUs), 8 x NVIDIA H20-3e, driver 580.105.08, five visible mlx5 HCAs, GCC 13.3, Release build,USE_TENT=ON,USE_CUDA=OFF.Results:
receiver_credit_test,receiver_credit_control_test,receiver_credit_dispatch_test,admission_queue_test,tent_runtime_queue_dispatch_test);Conclusion: this rerun supports the protocol/state-machine and dispatch-boundary feasibility claims: receiver capacity can be enforced as an atomic pre-submit condition without changing the disabled path. It is not evidence of RDMA throughput or TTFT improvement. The remaining production gate is still a multi-sender real-RDMA incast experiment with receiver occupancy, data validation and healthy-path utilization.
@catyans commented on GitHub (Jul 14, 2026):
Production adaptive dispatch follow-up (2026-07-14)
This is follow-up prototype evidence, not a result from the current #2859 or #2862 PR heads. The tested code is on
receiver-credit-production-control, latest commitbbad07ab.The prototype adds an engine-independent adaptive dispatch limiter shared by the credit pull controller and runtime queue. The RPC callback publishes only an atomic effective-owner limit; the runtime queue applies the smaller of its configured static limit and the adaptive limit. Defaults are conservative: initial/max owner window 2, 20 ms stall threshold, multiplicative decrease, additive recovery after 512 healthy pulls, two-event hysteresis before permanently lowering the learned ceiling, and a hard clamp to
runtime_queue/max_dispatch_owners.Final real-RDMA validation
Two-node H20/RoCE cluster, one NUMA node, one reachable rail (
mlx5_0), four submit threads, 4 MiB WRITE, batch size 1, 20-second measurement interval:The default adaptive case was within +0.19% of the matched no-credit/static-2 baseline, with no pull over 100 ms. Starting from the known-unsafe level 3 improved throughput by 106.82% over the old static-3 behavior and reduced >100 ms pulls from 12 to 2 while learning and recovering to ceiling 2.
Remote regression guard: 9/9 suites passed (
receiver_credit_test, control, protocol, allocator, config, control service, controller, dispatch, and runtime-queue dispatch).Claims boundary
This supports automatic recovery from a persistently unsafe dispatch window without a measured healthy-path throughput regression in this setup. It does not guarantee zero first-loss when an operator explicitly raises the ceiling above a validated level: the two-event hysteresis can still observe the first two roughly 200 ms RTO samples before learning the lower ceiling. Preventing even that first control-path loss requires the independent typed control QP / reserved WR-CQ budget proposed by this RFC.
A separate reviewable follow-up PR is still required; these results must not be attributed to the existing #2859 or #2862 code until that branch is opened and reviewed.
Control RPC cost and tail evidence
The production-shaped path measured the actual
ControlClient -> ControlService -> ReceiverCreditAllocatorexchange between two RDMA nodes, rather than a fake/in-process channel.A two-resource credit request is 136 bytes and its response is 148 bytes at the credit codec layer. On the captured TCP wire, the framed request/response were 167/171 bytes. The unloaded result shows that codec and allocator work are not the healthy-path bottleneck.
When TCP control RPC and RDMA WRITE shared
mlx5_0, the result depended strongly on data dispatch concurrency:Dual-endpoint packet capture established the approximately 200 ms tail mechanism: the sender capture contained the initial 167-byte framed request, but the receiver did not see that TCP sequence until retransmission 201--207 ms later. After receipt, the receiver generated the 171-byte response in about 10--50 us. Linux reported a 200 ms minimum TCP RTO. This rules out allocator CPU, receiver lock contention, and coroutine execution as the long-tail source in the sampled events; the control packet was lost after the sender capture point while TCP control and RDMA payload shared the mlx5 path.
With the safe two-owner window, three alternating baseline/credit repetitions gave median throughput 39.56/40.76 GB/s. The three credit runs had maximum pull latency 722/592/584 us, with no pull above 1 ms. Marking the TCP flow DSCP 46 did not fix the unsafe/unbounded case on this VPC.
Therefore the measured RPC conclusion is: healthy control RPC overhead is small, but TCP does not provide independent progress when the shared RDMA path is overdriven. The adaptive limiter prevents repeated collapse after detecting an unsafe level; a separate typed control QP with reserved WR/CQ capacity remains the durable design for preventing even the first shared-path TCP loss.
Benefit assessment
The primary benefit is receiver-capacity safety under many-to-one load; throughput improvement is a secondary overload-recovery benefit.
Operationally, the design turns aggregate receiver overload into an explicit bounded credit stall before transport handoff, rather than allowing independent senders to bury excess work in RDMA, staging, or receiver queues. Under a validated safe window, this protection was obtained without a measurable throughput penalty; when deliberately started at an unsafe level, adaptive control avoided the persistent throughput collapse of a static setting.
The evidence does not yet establish a numerical SGLang/vLLM TTFT or application SLO improvement. It also does not guarantee zero first-loss after an operator raises the ceiling above a validated level. Those claims require an application-level experiment and, for independent control progress, the proposed typed control QP with reserved WR/CQ capacity.
@alogfans commented on GitHub (Jul 16, 2026):
Thanks for the detailed proposal and validation. However, we do not recommend proceeding with the RPC-based receiver-credit design at this stage.
Our primary concern is system complexity. This proposal introduces a new distributed control protocol into the critical transfer path, including additional RPCs, credit allocation and reconciliation, session/epoch fencing, capability negotiation, stale-state handling, retries, timeout recovery, restart recovery, and compatibility/fallback modes. These mechanisms substantially increase the number of system states and failure combinations that we need to reason about, test, and maintain.
More importantly, once credit RPC becomes part of dispatch eligibility, the availability of the data path also depends on the availability and correctness of this new control path. The existing experiments already show that the RPC path may experience TCP retransmission delays when sharing resources with RDMA traffic. Adaptive limiting mitigates some symptoms, but adds another stateful feedback mechanism rather than removing the underlying complexity.
We acknowledge that receiver-side capacity control addresses a real many-to-one overload problem. Nevertheless, at the current stage, we prefer simpler sender-local admission and dispatch-window controls. They may provide weaker global guarantees, but their behavior, operational cost, and failure modes are considerably easier to understand and maintain.
Therefore, we suggest keeping the benchmark and protocol-model work for future reference, while pausing the runtime dispatch-gate and production RPC integration. We can revisit this direction if simpler mechanisms prove insufficient and the additional protocol complexity is clearly justified by application-level benefits.
@catyans commented on GitHub (Jul 21, 2026):
Thanks for the clear direction. Agreed — I will pause the runtime dispatch-gate and production RPC integration work in #2862 and #2925, and keep the benchmark and protocol-model work only as reference. I will refocus any follow-up on simpler sender-local admission and dispatch-window controls, without introducing a new distributed control-path dependency.
@catyans commented on GitHub (Jul 31, 2026):
Closing as not planned after the maintainer decision above. The RPC-based receiver-credit PR stack has been closed; future work will focus on simpler sender-local admission and dispatch-window controls without a new distributed control-path dependency.