You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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:
sender submit
-> local queue admission
-> local scheduling / dispatch window
-> Transport::submitTransferTasks() or ProxyManager::submit()
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:
bytes that may safely be in flight;
logical request/completion slots;
remote staging chunks;
consumer readiness or Decode-side commit capacity;
receiver CPU/QP/CQ progress capacity.
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
Bound aggregate many-to-one work by receiver capacity.
Apply backpressure before work is buried in transport or ProxyManager queues.
Support Direct and Staged plans with different resource charges.
Make duplicate, reordered, stale, and post-restart credit updates safe.
Preserve old-peer and default-disabled behavior.
Expose structured backpressure to SGLang/vLLM without blocking scheduler threads.
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:
Receiver-capacity safety for modeled resources. The sum of outstanding reservations across senders cannot exceed the receiver's configured capacity for DATA_BYTES, REQUEST_SLOTS, STAGING_SLOTS, and CONSUMER_SLOTS. A resource that is not modeled or charged is not covered by this guarantee.
Early, bounded backpressure. Work that lacks receiver capacity remains in the sender's bounded runtime queue instead of being buried in transport, ProxyManager, or remote-staging queues. This converts receiver overload into explicit credit stall rather than unbounded downstream accumulation.
Atomic multi-resource eligibility. A transfer is dispatched only after its complete charge vector is reserved. Partial reservations, such as bytes without a staging or consumer slot, are never exposed.
Staged-path progress isolation. Separately bounded control and staging-internal capacity prevents user payload from consuming the resources required to return credit or complete the inner work that releases an outer reservation.
Replay and restart safety. Duplicate, reordered, decreasing, overflowing, stale-epoch, and pre-restart updates cannot mint additional usable credit. Old sessions are fenced before their resources are reused.
Control-plane recovery at zero data credit. Credit, heartbeat, reset, cancel, and health traffic can make progress through a bounded control budget even when payload dispatch is stopped.
Actionable pressure attribution. The runtime can distinguish receiver-credit, local-queue, staging, and transport pressure. A later connector can use that typed signal to pause, retry, select another Decode node, fallback, or recompute instead of treating every condition as generic TooManyRequests.
Incremental deployment. Default-disabled behavior remains unchanged, capability negotiation supports mixed-version peers, and required mode fails or falls back explicitly rather than treating missing credit as unlimited capacity.
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
A second WDRR/priority scheduler.
Per-RTT congestion control or replacement of DCQCN/HPCC/ECN/PFC.
A cluster-wide per-transfer executor.
Revoking already-dispatched work.
Solving tenant authentication or QoS contract policy in this RFC.
Reusing Status::TooManyRequests without identifying the pressure source.
Terminology and Direction
receiver means the node whose memory/resource is receiving payload, not necessarily the API target:
Plan
Data receiver
Remote receiver credit?
RDMA WRITE to peer
remote target
yes
remote staging/delegate
remote staging/consumer
yes
RDMA READ from peer
local initiator
no; apply local resource limits
local SHM/NVLink
local process/host
local accounting only
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:
session_id and epoch match the active peer session;
sequence is newer than the last accepted update;
every cumulative grant is non-decreasing;
subtraction and addition pass overflow/underflow checks.
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.
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.
Direct WRITE: DATA_BYTES + REQUEST_SLOTS + CONSUMER_SLOTS.
Remote staged WRITE: the above plus STAGING_SLOTS.
Local RDMA READ receive: local resource accounting, no remote ledger.
Proposed Wire Schema
The exact serialization can change, but the semantic fields must remain explicit and versioned:
structReceiverCreditUpdateV1 {
uint16_t schema_version{1};
uint16_t flags{0};
uint32_t qos_class{0};
UInt128 receiver_session_id;
uint64_t epoch{0};
uint64_t sequence{0};
// Freshness hint, evaluated from the sender's local receive time.// It is not permission for the receiver to reclaim resources.uint32_t freshness_ttl_ms{0};
std::vector<CreditAmount> grants;
};
structSenderCreditUsageV1 {
uint16_t schema_version{1};
UInt128 receiver_session_id;
uint64_t epoch{0};
uint64_t sequence{0};
uint32_t qos_class{0};
std::vector<CreditAmount> consumed_totals;
std::vector<CreditAmount> pending_demand;
};
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
Disabled ------------------------------> Legacy
|
v
Negotiating -- unsupported -----------> Legacy or Failed(required)
|
+-- compatible --------------------> Active
|
stale/disconnect ----+----> Stale
| |
| +-- newer update -> Active
|
new session/epoch ---+----> Fencing -> Active(new epoch)
Rules:
Active: new dispatch may reserve credit.
Stale: stop new credit-gated dispatch; existing transport-owned work continues to terminal state.
New epoch: invalidate unused old ledger state, but do not reuse old target resources until old QPs/inflight work are fenced.
Credit violation: fail/quarantine the peer and emit a protocol error metric; do not silently overdraw.
Counter overflow, unknown required resource, malformed schema, or decreasing cumulative total: reject the update.
Dispatch Integration
Receiver credit belongs at dispatch eligibility, not queue admission:
TryAdmit(local owners/bytes/lifetime)
-> PickByLocalPolicy(priority/deadline/deficit)
-> CreditLedger::TryReserve(plan.credit_charge)
success: hand off to transport/proxy
blocked: keep queued; record stall; request/update demand
The current pickForDispatch() transitions selected owners to Dispatching. A production integration therefore needs one agreed boundary:
an eligibility callback/filter before state transition; or
a safe 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:
effective dispatch = local window
AND local scheduler eligibility
AND receiver credit for every required resource
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:
outer staged work reserves receiver capacity
-> inner staging transfer waits for the same exhausted class
-> outer work cannot complete and release capacity
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:
current notifications are string name/msg payloads;
peer/session identity must be bound to the endpoint, not trusted from payload text;
publisher enqueue must be nonblocking;
updates need periodic refresh and failure metrics;
control WR/CQ slots must remain available under payload saturation.
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
Alternative
Advantages
Risks / limitations
Recommendation
Extend BootstrapDesc
Reuses peer setup and capability negotiation
Bootstrap is not a recurring update channel; reconnect semantics become overloaded
Capability/version negotiation only
Typed ControlService RPC
Explicit schema, identity and error reporting; easy to fault-inject
Requires a bounded async client/server path and independent progress guarantees
Preferred first production transport
Typed notification message
Reuses the existing notification QP and configured control SL/TC
Current notification payload is string-based and its retry/identity contract is insufficient
Follow-up after typed framing and reserved WR/CQ budget
Piggyback on data completion
Low message count
Credit cannot recover when payload is stopped; creates a circular dependency
Do not use as the only update path
The 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
TooManyRequests currently 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_AFTER so SGLang/vLLM can pause submission, choose another Decode node, fallback, or recompute locally.
Validate schema/version/length before constructing runtime types
Dispatch eligibility adapter
adjacent to Admission/runtime queue implementation
Gate transport handoff without changing queue admission semantics
Metrics
existing bounded TENT metrics registry
Stall, stale update, protocol violation and capacity gauges
Concurrency rules:
Control callbacks validate framing and enqueue updates into a bounded queue; they do not mutate Admission state or call a transport while holding a credit lock.
The runtime/dispatch owner drains updates and applies each credit-key mutation serially, or under a dedicated ledger shard lock if the current thread model requires it.
Multi-resource reserve is all-or-nothing under one ledger critical section. A partial reservation is never externally visible.
Receiver capacity allocation and resource release use the same ownership domain. Grant publication happens after reservation is committed.
No credit mutex may be held across transport submission, user callbacks, RPC, notification send, or blocking wait.
Completion only reports receiver resource release; it must not mint sender-side credit locally.
Memory is bounded by configured limits, not request count:
ledger entries <= max_peers * max_qos_classes * negotiated_resources
queued control updates <= control_queue_capacity
wire resources per update <= max_resources_per_update
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
A sender cannot self-assign a QoS class or increase a grant; peer identity and authorized class come from the negotiated endpoint/policy context.
A receiver never grants more than committed capacity, even if demand reports are forged or duplicated.
Per-peer demand, update frequency and control-queue occupancy are bounded to prevent control-plane memory or CPU exhaustion.
Malformed, decreasing, overflowing or wrong-epoch updates fail closed and increment a bounded protocol-error metric.
A disconnected sender cannot retain reservations indefinitely: explicit return is preferred, while session fencing provides the safe reclamation boundary.
Optional legacy mode is a rollout policy, not an implicit unlimited grant. Required mode must reject or take an explicit configured fallback.
Debug traces may contain peer/request identifiers, but metrics must not use unbounded identifiers as labels.
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 4188e3a with the parent of #1971 (c3bafbed) on an 8×H20 host with four ~392 Gbit/s RDMA rails.
A single-rail isolation matrix passed 15/15 runs with wrong=0.
The same high-risk cases on four rails had 5/15 abnormal runs: two silent data-corruption results and three P2P operation failures.
A valid 32/64/128 MiB pool/chunk matrix passed 9/10 runs; one 64 MiB pool + 8 MiB chunk run lost progress and hit a 150-second watchdog.
CUDA failed-rank and replacement-recovery tests both passed.
A two-node run published the correct RDMA IPs but could not establish PG connectivity because the peer store value was shorter than 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.md beside this draft.
Implementation Plan
PR 1 — Credit model and protocol invariants
private CreditKey, CreditLedger, CreditUpdateV1;
deterministic tests for duplicate, reorder, sequence gaps, stale epoch, reset, overflow, underflow, multi-resource atomic reserve and return;
no background thread, network messages, Admission changes, or behavior change.
PR 2 — Capability and control transport
version negotiation and optional/required rollout mode;
fake transport fault injection: loss, delay, duplication, reordering, disconnect;
endpoint-bound peer identity;
bounded nonblocking publisher and freshness metrics.
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 [TENT] add priority and byte credits to runtime queue dispatch #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...