Skip to content

Commit 0eb3eaa

Browse files
authored
docs: clarify decode-block rejection prerequisites and router tracking distinction (#6972)
Signed-off-by: athreesh <anish.maddipoti@utexas.edu>
1 parent 59e76f4 commit 0eb3eaa

1 file changed

Lines changed: 70 additions & 1 deletion

File tree

docs/fault-tolerance/request-rejection.md

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,22 @@ When all workers exceed their configured busy thresholds, new requests receive a
4343

4444
### Frontend Arguments
4545

46-
Configure busy thresholds when starting the frontend. `--admission-control token-capacity` is required to activate the thresholds; the default (`none`) leaves them disabled.
46+
Configure busy thresholds when starting the frontend. `--admission-control token-capacity` is required to activate the thresholds; the default (`none`) leaves them disabled. For decode-block rejection, start the frontend in KV router mode so the worker load metrics path is active.
4747

4848
```bash
4949
python -m dynamo.frontend \
5050
--admission-control token-capacity \
51+
--router-mode kv \
5152
--active-decode-blocks-threshold 0.85 \
53+
--router-track-output-blocks \
5254
--active-prefill-tokens-threshold 10000
5355
```
5456

5557
| Argument | Type | Description |
5658
|----------|------|-------------|
59+
| `--router-mode kv` | enum | Required for decode-block rejection. Initializes the KV router worker-load plumbing that receives `active_decode_blocks` updates. Without KV mode, `--active-decode-blocks-threshold` will not produce 529s based on decode-block load. |
5760
| `--active-decode-blocks-threshold` | float (0.0-1.0) | KV cache block utilization threshold |
61+
| `--router-track-output-blocks` | bool | Include generated output tokens in the router's active block count. Enable this for long-output workloads; otherwise only prompt/input blocks are counted and a long generation can fill KV without crossing the threshold seen by the router. |
5862
| `--active-prefill-tokens-threshold` | int | Prefill token count threshold |
5963
| `--active-prefill-tokens-threshold-frac` | float | Prefill token threshold as a fraction of `max_num_batched_tokens` |
6064
| `--admission-control` | `token-capacity` \| `none` | Admission control mode. `token-capacity` applies the busy thresholds above; `none` (the default) clears them while leaving router queueing controlled by `--router-queue-threshold`. To enable busy-worker admission, you must pass `--admission-control token-capacity` |
@@ -63,6 +67,8 @@ python -m dynamo.frontend \
6367

6468
Thresholds can be adjusted at runtime via the `/busy_threshold` endpoint:
6569

70+
The API updates threshold values only. It does not enable `--admission-control token-capacity`, `--router-mode kv`, or `--router-track-output-blocks`; those must be set when the frontend starts.
71+
6672
#### Set Thresholds
6773

6874
```bash
@@ -98,6 +104,36 @@ Response:
98104

99105
Workers are marked as "busy" based on a dual-threshold system. A worker is considered busy when **either** threshold is exceeded.
100106

107+
### Decode-Block Rejection Requirements
108+
109+
Decode-block based rejection (`active_decode_blocks_threshold`) only works when all of these conditions are true:
110+
111+
1. The frontend is started with `--admission-control token-capacity`.
112+
2. The frontend is started with `--router-mode kv`.
113+
3. A decode-block threshold is configured (`--active-decode-blocks-threshold` or `/busy_threshold` API).
114+
4. The frontend `KvWorkerMonitor` is receiving worker load events (`ActiveLoad`).
115+
5. Workers are publishing `active_decode_blocks`.
116+
6. Worker runtime config provides `kv_total_blocks` so utilization ratio can be computed.
117+
7. For long-output workloads, `--router-track-output-blocks` is enabled so generated tokens add output blocks to the active block count.
118+
119+
If any prerequisite is missing, decode-block busy detection is effectively disabled for those workers. The most common production symptom is that `--active-decode-blocks-threshold` appears to be accepted but no HTTP 529 is returned when KV fills up.
120+
121+
Examples of missing prerequisites:
122+
123+
- Frontend was launched without `--router-mode kv`; the NATS/event client path used for worker load metrics is not initialized, so `active_decode_blocks` updates are not consumed and the threshold is ignored.
124+
- Frontend cannot receive events because worker-load subscription is unavailable (for example, event transport not reachable or misconfigured).
125+
- Workers are running in a mode/path that does not publish `active_decode_blocks` (for example, custom integrations without worker metrics publishing).
126+
- Output-heavy traffic is served without `--router-track-output-blocks`; only prompt/input blocks are reflected in the router's active block accounting, so generated output blocks can exhaust KV before triggering decode-block rejection.
127+
128+
### Important: Different from `router_track_active_blocks`
129+
130+
`active_decode_blocks_threshold` and `router_track_active_blocks` are related to load, but they are not the same feature:
131+
132+
- `active_decode_blocks_threshold` drives busy/free worker classification and request rejection (HTTP 529 when all workers are busy).
133+
- `router_track_active_blocks` controls KV router internal block bookkeeping used for routing decisions.
134+
135+
In disaggregated setups, prefill routing intentionally disables `router_track_active_blocks`; this does **not** disable decode-block rejection for decode workers.
136+
101137
### KV Cache Block Threshold
102138

103139
Monitors the percentage of KV cache blocks in use:
@@ -213,6 +249,13 @@ dynamo_frontend_model_rejection_total{endpoint="chat_completions",model="Qwen/Qw
213249
dynamo_frontend_model_rejection_total{endpoint="completions",model="Qwen/Qwen3-0.6B"} 5
214250
```
215251

252+
For decode-block rejection debugging, also inspect:
253+
254+
| Metric | Type | Description |
255+
|--------|------|-------------|
256+
| `dynamo_frontend_worker_active_decode_blocks` | Gauge | Latest active decode blocks per worker and DP rank |
257+
| `dynamo_frontend_worker_active_prefill_tokens` | Gauge | Latest active prefill tokens per worker and DP rank |
258+
216259
**Endpoint:** Available on the frontend HTTP service at `/metrics`.
217260

218261
## Tuning Thresholds
@@ -293,6 +336,32 @@ If using Kubernetes HPA, ensure rejection thresholds trigger before autoscaling:
293336
--active-decode-blocks-threshold 0.85
294337
```
295338

339+
## Troubleshooting
340+
341+
### Decode-block rejection not triggering
342+
343+
1. Confirm threshold is actually set:
344+
```bash
345+
curl -s http://localhost:8000/busy_threshold
346+
```
347+
2. Confirm the frontend was started with `--admission-control token-capacity`.
348+
3. Confirm the frontend was started with `--router-mode kv`.
349+
4. For long-output workloads, confirm the frontend was started with `--router-track-output-blocks`.
350+
5. Verify frontend is receiving worker load updates:
351+
```bash
352+
curl -s http://localhost:8000/metrics | grep dynamo_frontend_worker_active_decode_blocks
353+
```
354+
6. Check frontend logs for worker-monitor subscription issues (for example, warnings that KV metrics subscriber is unavailable).
355+
7. Verify worker `kv_total_blocks` is present (runtime config / worker metrics), for example:
356+
```bash
357+
curl -s http://<worker-system-port>/metrics | grep dynamo_component_total_blocks
358+
```
359+
8. Verify event transport configuration between frontend and workers (`--event-plane`, NATS/ZMQ connectivity).
360+
361+
### Common confusion: `router_track_active_blocks`
362+
363+
If `active_decode_blocks_threshold` is configured but you suspect `router_track_active_blocks` is the blocker, treat that as a separate routing knob. Busy rejection depends on worker load events and threshold configuration, not on the router's internal active-block tracking flag.
364+
296365
## Worker-Side Request Admission
297366

298367
In addition to the frontend's metric-driven busy detection above, a worker can

0 commit comments

Comments
 (0)