Skip to content

[vllm] fix: prevent scheduler resume race during async weight sync - #7846

Merged
wuxibin89 merged 1 commit into
verl-project:mainfrom
lxb007981:fix_vllm_dp_resume_gate_race
Sep 18, 2026
Merged

wuxibin89 merged 1 commit into
verl-project:mainfrom
lxb007981:fix_vllm_dp_resume_gate_race

Conversation

@lxb007981

@lxb007981 lxb007981 commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Fully async weight synchronization can deadlock when a duplicate scheduler resume overlaps with the start of a new DP request wave.

Restoring the KV cache implicitly resumes vllm scheduling, and the subsequent resume_generation() call resumes it again. In the current vLLM version, resume_scheduler() branches on the rank-local engines_running flag. A new request wave can cause some ranks to skip the resume collective while others enter it, leaving EngineCores and model workers waiting on each other until Gloo times out.

Split replica resume into two ordered phases:

  • Keep request admission closed while the head server resumes all DP EngineCores and waits for the resume RPC to complete.
  • Open submission gates on all servers only after engine resume returns.

This prevents waiting generation tasks from starting a new DP wave while the duplicate resume is still in progress. Also order the server-level resume_generation() wrapper to resume engines before opening its gate.

Keep the explicit resume because it is still required when KV-cache restoration is skipped.

See here for detailed walkthrough about the race

There is a race that triggers a deadlock during weight synchronization in fully async mode. Some ranks are blocked inside DPEngineCoreProc.resume_scheduler(), while some ranks are waiting inside model execution until they both timeout. The root cause is the race between duplicate scheduler resume and the start of a new DP request wave.

During each weight update, verl calls the same vLLM DPEngineCoreProc.resume_scheduler() method twice:

  1. Implicitly, when wake_up(tags=["kv_cache"]) restores the remaining memory and resumes scheduling.
  2. Explicitly, through the subsequent resume_generation() call.

The affected vLLM implementation is not idempotent: a duplicate resume can enter a collective even though scheduling is already unpaused. It branches on the rank-local engines_running flag. Since request-wave notifications and resume RPCs arrive independently, one rank can see True and return immediately, while another sees False and enters the resume barrier. That creates a circular wait:

  • B’s EngineCore waits for A’s EngineCore to join the resume collective.
  • A’s workers wait for B’s workers to join the model collective.
  • B’s EngineCore cannot dispatch work to its workers while blocked in the resume collective.

This deadlock eventually reported as Gloo timeouts.

vLLM PR #55229, currently a draft, addresses this vllm issue by recognizing duplicate resumes using scheduler pause state and the wave gate. Already-unpaused ranks skip synchronization consistently, while genuine resumes retain the barrier.

Related PRs: #7511

Checklist Before Starting

  • Search for similar PRs. Paste at least one query link here: ...
  • Format the PR title as [{modules}] {type}: {description} (This will be checked by the CI)
    • {modules} include fsdp, megatron, veomni, sglang, vllm, rollout, trainer, ci, training_utils, recipe, hardware, deployment, ray, worker, single_controller, misc, perf, model, algo, env, tool, ckpt, doc, data, cfg, reward, fully_async, one_step_off
    • If this PR involves multiple modules, separate them with , like [megatron, fsdp, doc]
    • {type} is in feat, fix, refactor, chore, test
    • If this PR breaks any API (CLI arguments, config, function signature, etc.), add [BREAKING] to the beginning of the title.
    • Example: [BREAKING][fsdp, megatron] feat: dynamic batching

Test

Verified under verl.experimental.fully_async_policy.fully_async_main x fully_async_ppo_megatron_trainer.yaml presets, fully async training on GLM-5.2.

API and Usage Example

No APIs changed.

@lxb007981
lxb007981 force-pushed the fix_vllm_dp_resume_gate_race branch from 837f40f to 2396c83 Compare September 14, 2026 07:24
@lxb007981 lxb007981 changed the title fix vllm dp resume race on verl side [vllm] fix: prevent scheduler resume race during async weight sync Sep 14, 2026
@lxb007981
lxb007981 marked this pull request as ready for review September 14, 2026 07:41
@wuxibin89
wuxibin89 requested a review from aoshen02 September 14, 2026 15:07
@lxb007981
lxb007981 force-pushed the fix_vllm_dp_resume_gate_race branch from 2396c83 to 673561a Compare September 16, 2026 02:27
Fully async weight synchronization can deadlock when a duplicate
scheduler resume overlaps with the start of a new DP request wave.

Restoring the KV cache implicitly resumes scheduling, and the subsequent
resume_generation() call resumes it again. In affected vLLM versions,
resume_scheduler() branches on the rank-local engines_running flag.
A new request wave can cause some ranks to skip the resume collective
while others enter it, leaving EngineCores and model workers waiting on
each other until Gloo times out.

Split replica resume into two ordered phases:
- Keep request admission closed while the head server resumes all DP
  EngineCores and waits for the resume RPC to complete.
- Open submission gates on all servers only after engine resume returns.

This prevents waiting generation tasks from starting a new DP wave while
the duplicate resume is still in progress. Also order the server-level
resume_generation() wrapper to resume engines before opening its gate.

Keep the explicit resume because it is still required when KV-cache
restoration is skipped.
@lxb007981
lxb007981 force-pushed the fix_vllm_dp_resume_gate_race branch from 673561a to e573a97 Compare September 17, 2026 09:41
@wuxibin89
wuxibin89 merged commit e2ac8f6 into verl-project:main Sep 18, 2026
86 of 112 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants