Request Rejection

Configure independent load thresholds that shed traffic with HTTP 529 before worker latency or memory use becomes unsafe.
View as Markdown

Request rejection (load shedding) rejects new requests when every eligible worker is overloaded, rather than accepting work that could exhaust GPU memory or degrade latency for in-flight requests. Dynamo returns HTTP 529 for overload by default so clients can distinguish an available but busy service from HTTP 503, which indicates that the service is unavailable.

Rejection is off by default. Each threshold is independently opt-in: setting one numeric threshold enables only that check. You do not need --admission-control; that compatibility flag is ignored.

How it works: The busy-detection formulas, data-parallel rank aggregation, worker-load event processing, and worker-side overflow queue are documented in Request Rejection Architecture.

1

Choose the load signals

Use the signals that match the workers you want to protect:

  • Set --active-decode-blocks-threshold for decode workers. The value is the fraction of available KV cache blocks in use, from 0.0 through 1.0.
  • Set --active-prefill-tokens-threshold for an absolute prefill-token limit.
  • Set --active-prefill-tokens-threshold-frac when the prefill limit should scale with the worker’s max_num_batched_tokens value.

The checks use OR logic. If you configure more than one threshold, exceeding any configured threshold marks that data-parallel rank as busy. A worker is rejected only after all of its ranks are busy, and a request is shed only when every eligible worker is busy.

Start conservatively, observe the rejection rate and latency, and then raise the limits. For example, a decode-block threshold of 0.75 sheds earlier than 0.90.

2

Enable rejection on the Frontend

Configure thresholds on the Frontend component. Decode-block rejection also requires KV router mode because that mode initializes the worker-load metrics path. For long-output workloads, enable output-block tracking so generated tokens contribute to the observed KV cache load.

1- name: Frontend
2 type: frontend
3 replicas: 1
4 podTemplate:
5 spec:
6 containers:
7 - name: main
8 image: ${RUNTIME_IMAGE}
9 command:
10 - python3
11 - -m
12 - dynamo.frontend
13 args:
14 - --router-mode
15 - kv
16 - --active-decode-blocks-threshold
17 - "0.85"
18 - --router-track-output-blocks
19 - --active-prefill-tokens-threshold
20 - "10000"

--router-track-output-blocks is especially important for long generations. Without it, a workload can fill KV cache with generated tokens without crossing the load value observed by the router.

You can configure just one signal. For example, a prefill-only deployment can omit the decode-block threshold and KV-specific options. To disable rejection, remove all three threshold settings or set their environment-variable values to None.

See Frontend Configuration for the complete flag, environment-variable, and validation reference.

3

Adjust thresholds at runtime

Optional. Use the Frontend admin API to change thresholds for a discovered model without redeploying. The API is available when DYN_ENABLE_FRONTEND_ADMIN_API=true, which is the default.

$kubectl port-forward svc/<deployment-name>-frontend 8000:8000 -n ${NAMESPACE}

Set one or more thresholds:

$curl -X POST http://localhost:8000/busy_threshold \
> -H "Content-Type: application/json" \
> -d '{
> "model": "Qwen/Qwen3-0.6B",
> "active_decode_blocks_threshold": 0.85,
> "active_prefill_tokens_threshold": 10000
> }'

Read the stored thresholds:

$curl http://localhost:8000/busy_threshold

A numeric value enables only its corresponding check. The router applies the new configuration when the next worker-load or runtime-configuration update arrives, so the change might not affect a routing decision immediately. This endpoint does not enable --router-mode kv or --router-track-output-blocks; set those when the Frontend starts.

4

Add a worker-side hard cap

Optional. A worker can independently cap concurrent engine work and queue only a small burst. Set --engine-request-limit N (or DYN_ENGINE_REQUEST_LIMIT) on the worker component:

1- name: VllmWorker
2 type: worker
3 podTemplate:
4 spec:
5 containers:
6 - name: main
7 args:
8 - --engine-request-limit
9 - "32"

When all N engine slots and the overflow queue are full, the worker rejects the request and the Frontend returns HTTP 529. DYN_DYNAMO_REQUEST_QUEUE_LIMIT controls the advanced overflow-queue size, defaults to 16, must be at least 2, and has an effect only when the engine limit is set. The effective cap is N + Q in-flight requests per worker.

See Runtime Configuration for the exact fields and Worker-Side Request Admission for the queue implementation.

5

Verify rejection

Inspect the configured thresholds and worker-load metrics:

$curl -s http://localhost:8000/busy_threshold
$curl -s http://localhost:8000/metrics \
> | grep -E 'dynamo_frontend_worker_active_(decode_blocks|prefill_tokens)'

Generate enough load to exceed the configured threshold, then confirm:

  • The client receives HTTP 529.
  • dynamo_frontend_model_rejection_total increases for the affected model and endpoint.
  • Worker-side hard-cap tests increase dynamo_rejection_request_total when both the engine and queue are full.

For all metric fields and labels, see Cancellation and Rejection.

Troubleshoot Decode-Block Rejection

If decode-block load does not produce HTTP 529 responses:

  1. Confirm that GET /busy_threshold shows a numeric active_decode_blocks_threshold for the model.

  2. Confirm that the Frontend started with --router-mode kv.

  3. For long-output workloads, confirm that the Frontend started with --router-track-output-blocks.

  4. Check that the Frontend receives worker updates:

    $curl -s http://localhost:8000/metrics \
    > | grep dynamo_frontend_worker_active_decode_blocks
  5. Check Frontend logs for worker-monitor subscription warnings.

  6. Confirm that each worker publishes its total KV block count:

    $curl -s http://<worker-system-port>/metrics \
    > | grep dynamo_component_total_blocks
  7. Verify the event-plane configuration and connectivity between the Frontend and workers.

--router-track-active-blocks is a separate routing option. Busy rejection depends on configured thresholds and worker-load events; it does not require that internal router tracking flag.

Configure Client Retries

Retry HTTP 529 responses with exponential backoff and jitter. Do not retry every HTTP 503 as if it were overload; 503 indicates that Dynamo currently has no available service path.

1import random
2import time
3
4
5def send_with_retry(request, max_retries=5):
6 for attempt in range(max_retries):
7 response = send_request(request)
8 if response.status_code != 529:
9 return response
10 wait_time = min(60, (2**attempt) + random.uniform(0, 1))
11 time.sleep(wait_time)
12 raise RuntimeError("maximum retries exceeded")

If an existing client only understands 503 retry semantics, set DYN_HTTP_OVERLOAD_STATUS_CODE=503 on the Frontend. The variable accepts any valid HTTP status code; an invalid value falls back to 529.