Describe the Bug
Currently when a replica is shed, all in-flight and queued requests are aborted a fixed 5 s after SIGTERM (5 s = default DYN_GRACEFUL_SHUTDOWN_GRACE_PERIOD_SECS). Clients get HTTP 500 "Failed to fold completions stream". There is no stage that waits for inflight requests to reach zero.
Steps to Reproduce
-
deploy the attached DGD
dgd-drain-repro.yaml
-
you can use your own load or use mine in the attachment to generate a trace: python3 make-trace.py --out trace.jsonl. This trace has 2 phases, phase 1 with TPS = 8.0 req/s, phase 2 with 0.2 req/s --> perfect to watch dynamo sheds replicas
-
replay the trace against the deployment:
FE=$(kubectl -n dynamo-system get svc drain-repro-frontend -o jsonpath='{.spec.clusterIP}') aiperf profile --model openai/gpt-oss-120b --tokenizer openai/gpt-oss-120b \ --endpoint-type completions --streaming --url http://$FE:8000 \ --input-file trace.jsonl --custom-dataset-type mooncake_trace \ --fixed-schedule --fixed-schedule-auto-offset \ --extra-inputs "ignore_eos:true" --artifact-dir ./aiperf-out
-
observe the responses of the clients during replica shedding.make-trace.py
Expected Behavior
a de-registered worker finishes its in-flight requests before exiting, bounded by a timeout
Actual Behavior
The worker de-registers from discovery correctly, so no new requests are routed to it. It then sleeps a fixed 5 s and aborts everything that are still generating. Clients receive HTTP 500.
Worker log of the shed replica (Dynamo 1.4.0, gpt-oss-120b):
16:41:50.979 Unregistering endpoint: namespace=dynamo-system-drain-repro ...
16:41:51.004 Successfully unregistered endpoint instance - worker removed from routing
16:41:51.005 Grace period 5.00s before stopping endpoints
16:41:56.009 Initiating runtime shutdown # exactly 5.004 s later
On the client side, at that same instant, 18 of 1500 requests fail simultaneously:
{"message":"Failed to fold completions stream for cc14fa97-...","type":"Internal Server Error"}
graceful_shutdown_with_discovery aborts without draining. Here is what happens chronologically during downscale:
Environment
- Dynamo: 1.4.0 same as the planner and vllm-runtime versions
- Inference engine: vllm-runtime 1.4.0 shipped by dynamo, based on vLLM 0.26.0
- model: gpt-oss-120b
- K8s: v1.34.8+k3s1
- OS: Ubuntu 24.04.3 (kernel 6.8.0-71)
- GPU: Nvidia h200
Additional Context
Just 2 FYIs:
-
there is a request draining feature but for dynamo rust runtime shutdown (here). This never kicks in because SIGTERM first calls shutdown_event, aborts in-flight requests and stops background loops. Only after that the rust runtime starts to shutdown, and start draining. But at that time there is no requests to be drained anymore.
-
(UPDATE: This is confirmed to be irrelevant after the discussion below) Dynamo already has a callbakc receiver: DynamoStatLoggerPublisher, whose record() receives a SchedulerStats every engine iteration (here), and that carries num_running_reqs and num_waiting_reqwhich might be useful for draining_callback
Screenshots
No response
Describe the Bug
Currently when a replica is shed, all in-flight and queued requests are aborted a fixed 5 s after SIGTERM (5 s = default
DYN_GRACEFUL_SHUTDOWN_GRACE_PERIOD_SECS). Clients getHTTP 500 "Failed to fold completions stream". There is no stage that waits for inflight requests to reach zero.Steps to Reproduce
deploy the attached DGD
dgd-drain-repro.yaml
you can use your own load or use mine in the attachment to generate a trace:
python3 make-trace.py --out trace.jsonl. This trace has 2 phases, phase 1 with TPS = 8.0 req/s, phase 2 with 0.2 req/s --> perfect to watch dynamo sheds replicasreplay the trace against the deployment:
FE=$(kubectl -n dynamo-system get svc drain-repro-frontend -o jsonpath='{.spec.clusterIP}') aiperf profile --model openai/gpt-oss-120b --tokenizer openai/gpt-oss-120b \ --endpoint-type completions --streaming --url http://$FE:8000 \ --input-file trace.jsonl --custom-dataset-type mooncake_trace \ --fixed-schedule --fixed-schedule-auto-offset \ --extra-inputs "ignore_eos:true" --artifact-dir ./aiperf-outobserve the responses of the clients during replica shedding.make-trace.py
Expected Behavior
a de-registered worker finishes its in-flight requests before exiting, bounded by a timeout
Actual Behavior
The worker de-registers from discovery correctly, so no new requests are routed to it. It then sleeps a fixed 5 s and aborts everything that are still generating. Clients receive
HTTP 500.Worker log of the shed replica (Dynamo 1.4.0, gpt-oss-120b):
On the client side, at that same instant, 18 of 1500 requests fail simultaneously:
{"message":"Failed to fold completions stream for cc14fa97-...","type":"Internal Server Error"}
graceful_shutdown_with_discoveryaborts without draining. Here is what happens chronologically during downscale:await asyncio.sleep(grace_period_s), default 5.0 sdrain_callback— skipped, vLLM passesNoneshutdown_event.set()engine_client.abort()runtime.shutdown()Environment
Additional Context
Just 2 FYIs:
there is a request draining feature but for dynamo rust runtime shutdown (here). This never kicks in because SIGTERM first calls
shutdown_event, aborts in-flight requests and stops background loops. Only after that the rust runtime starts to shutdown, and start draining. But at that time there is no requests to be drained anymore.(UPDATE: This is confirmed to be irrelevant after the discussion below) Dynamo already has a callbakc receiver:
DynamoStatLoggerPublisher, whose record() receives aSchedulerStatsevery engine iteration (here), and that carriesnum_running_reqsandnum_waiting_reqwhich might be useful fordraining_callbackScreenshots
No response