P/D Disaggregation¶
Prefill/decode (P/D) disaggregation separates prompt processing from token generation. LoxiLB Inference Gateway selects the two endpoint roles and runs the request sequence required by the configured serving engine.
Why separate prefill and decode?¶
- Prefill processes the input tokens and creates KV state. It is primarily compute intensive.
- Decode produces tokens iteratively while reading KV state. It is primarily memory-bandwidth sensitive.
P/D lets operators scale and tune those roles independently. It also adds network, engine-dialect, timeout, and failure-coupling requirements, so start with a converged pool unless the operational benefit is clear.
Shared topology¶
flowchart LR
CLIENT([Client]) --> GW["LoxiLB Inference Gateway<br/>mode 4"]
GW --> SELECT["Select prefill and decode"]
SELECT --> P["Prefill or CONTEXT<br/>ep_role: 1"]
SELECT --> D["Decode or GENERATION<br/>ep_role: 2"]
P -.->|Engine-specific KV transfer| D
D --> GW
GW --> CLIENT
style P fill:#e8f5e9,stroke:#43a047
style D fill:#e1f5fe,stroke:#0288d1
Every P/D rule requires:
mode: 4(fullproxy)pd_disagg_mode: true- at least one
ep_role: 1endpoint - at least one
ep_role: 2endpoint - one coherent
kvEngineTypefor the entire rule
The request flow depends on the engine¶
| Engine | Request order | Transfer contract | Engine-specific setting |
|---|---|---|---|
| vLLM | Sequential prefill, then decode | Gateway extracts kv_transfer_params; KV data moves through NIXL |
Endpoint nixl_port |
| SGLang | Concurrent prefill and decode | Gateway injects one bootstrap host/port/room triple into both requests | pdBootstrapPort |
| TensorRT-LLM | Sequential CONTEXT, then GENERATION | Gateway extracts and relays disaggregated_params; context may finish early |
Context and generation server roles |
| llama.cpp | Not supported | None | P/D configuration is rejected |
flowchart TD
REQ([P/D request]) --> ENGINE{kvEngineType}
ENGINE -->|vllm| V1[Send prefill]
V1 --> V2[Extract transfer parameters]
V2 --> V3[Send decode]
ENGINE -->|sglang| S1[Create bootstrap room]
S1 --> S2[Dispatch both legs concurrently]
S2 --> S3[Drain prefill and relay decode]
ENGINE -->|trtllm| T1[Send context_only]
T1 --> T2{Context finished?}
T2 -->|Yes| T3[Relay context response]
T2 -->|No| T4[Send generation_only]
style S2 fill:#e1f5fe,stroke:#0288d1
style T3 fill:#e8f5e9,stroke:#43a047
Do not copy fields across engines
nixl_port, pdBootstrapPort, ZMQ rank settings, and TensorRT-LLM's HTTP event drain solve different problems. A configuration that is valid for one engine can be rejected or meaningless for another.
Optional cache-aware layers¶
P/D itself does not require kvExactMode.
- P/D always applies session stickiness first and falls back to load-aware endpoint selection.
pd_cache_aware_mode: trueadds the radix-trie prefix-affinity tier between those two stages.kvExactMode: 1adds engine-exact cache inventory selection for eligible prefill or context endpoints.kvExactMode: 3is not a P/D option; it is reserved for role-less single pools.
For KV-exact mode, the tokenizer, model identity, block/page size, hash contract, and event transport must match the engine. A rule can continue serving through fallback routing even when this parity is broken, so verify metrics explicitly.
Shared configuration fields¶
| Field | Default | Meaning |
|---|---|---|
pd_disagg_mode |
false |
Enable the engine-specific P/D orchestrator. |
pd_cache_aware_mode |
false |
Add radix-trie prefix affinity to the P/D ladder. Session stickiness and load fallback do not depend on this flag. Requires P/D. |
pd_session_ttl_sec |
0 in the API |
Session lifetime in seconds. The data path converts 0 to its 300-second runtime default. |
pd_cache_threshold |
20 |
Minimum prefix-trie match percentage. |
pd_balance_abs_threshold |
3 |
Active-connection spread that bypasses the optional radix-trie choice and uses load fallback. |
kvExactMode |
0 |
1 adds P/D KV-exact selection; 3 is invalid with P/D. |
ep_role |
0 |
Endpoint role: 1 prefill/CONTEXT, 2 decode/GENERATION. |
nixl_port |
0 |
vLLM transfer side channel; 0 uses the target port. |
pdBootstrapPort |
0 |
SGLang prefill bootstrap port; 0 means 8998. |
See the Configuration Reference before copying these fields into a rule.
Minimal validation workflow¶
- Start every backend and wait for model readiness, not only process readiness.
- Prove ordinary fullproxy routing with health probes.
- Create one P/D rule with one endpoint of each role and without KV-exact mode.
- Test a non-streaming request and an SSE request.
- Confirm both roles handled the request using engine logs and Gateway metrics.
- Add more endpoints and exercise one endpoint failure at a time.
- Add cache-aware selection only after the base P/D flow is reliable.
Useful Gateway metric families include:
loxilb_ai_pd_requests_total
loxilb_pd_kv_tier15_hits_total
loxilb_pd_kv_blocks
loxilb_kv_subscriber_connected
loxilb_pd_sg_room_retry_total
loxilb_pd_trt_ctx_early_exit_total
Metric availability depends on the configured engine and optional routing layers.
Failure diagnosis¶
| Symptom | Check first |
|---|---|
| Rule creation fails | Fullproxy mode, both endpoint roles, and engine-specific field coherence |
| Client request hangs | Engine request order, role health, transfer reachability, and first-byte timeouts |
| Decode recomputes the prompt | Engine transfer configuration and side-channel/bootstrap connectivity |
| Traffic succeeds but KV-exact hits remain zero | Tokenizer, model, block size, hash algorithm, event feed, and inventory readiness; kvWarmupSec is currently inert |
| One role becomes overloaded | Endpoint health, session TTL, load threshold, and role counts |
| Errors continue to select one endpoint | Circuit-breaker enablement and origin/connect failure classification |
Circuit-breaker behavior¶
P/D rule creation enables a per-endpoint breaker with a three-connect-failure threshold and a
30-second open window in the current implementation. An enabled breaker also tracks
consecutive origin 5xx responses; the default origin threshold is three and
LLB_PD_ORIGIN_ERR_THRESHOLD=0 disables that path. The current 5xx response is relayed to the
client. Opening the breaker demotes the endpoint for later selection; it does not replay the
failed request. A 4xx response neither advances nor resets the origin-5xx streak.
Security considerations¶
- Isolate management, client, event, and engine-transfer networks according to their trust levels.
- Permit SGLang bootstrap or vLLM transfer ports only between the endpoints that need them.
- Do not expose TensorRT-LLM's destructive event endpoint to monitoring or untrusted consumers.
- Avoid logging request bodies or engine transfer objects when they may contain sensitive prompts or opaque state.
- P/D routing is not a tenant boundary. Apply authentication, authorization, and quota policies independently.
Evidence limits¶
Repository validation proves configuration guards and deterministic mock request paths. It does not establish production throughput, latency, cache benefit, engine-version compatibility, cross-node fault tolerance, or secure network policy for a particular deployment.