@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / 0f2f24c8b2
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 7, 16:30 UTC

[Bugfix] Fix MessageQueue connect_ip for cross-node data parallelism (#35429)

Signed-off-by: Lu Fang <fanglu@fb.com> Co-authored-by: Lu Fang <30275821+houseroad@users.noreply.github.com>

Lucia Fang committed Feb 26, 2026 at 14:08 UTC 0f2f24c8b205b5bf2dadacf1f95f1ad9f7de73e0
2 files changed +93 -1
tests/distributed/test_mq_connect_ip.py new
+79
@@ -0,0 +1,79 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""
4 +Test that MessageQueue uses the local node's IP for binding,
5 +not a remote master_addr. This validates the fix for cross-node
6 +data-parallel where each DP group leader must bind to its own IP.
7 +
8 +The bug: multiproc_executor used `parallel_config.master_addr` as
9 +`connect_ip` for every DP group's MessageQueue. For DP groups whose
10 +leader is NOT on the master node, binding to master_addr fails with
11 +"Cannot assign requested address".
12 +
13 +The fix: use `get_ip()` (local node IP) instead of `master_addr`.
14 +"""
15 +
16 +import pytest
17 +import zmq
18 +
19 +from vllm.distributed.device_communicators.shm_broadcast import MessageQueue
20 +from vllm.utils.network_utils import get_ip
21 +
22 +
23 +def test_mq_bind_with_local_ip():
24 + """MessageQueue with remote readers should successfully bind
25 + when connect_ip is the local node's IP."""
26 + # n_reader=2, n_local_reader=1 means 1 remote reader,
27 + # which triggers the remote ZMQ socket bind.
28 + mq = MessageQueue(
29 + n_reader=2,
30 + n_local_reader=1,
31 + connect_ip=get_ip(),
32 + )
33 + handle = mq.export_handle()
34 + assert handle.remote_subscribe_addr is not None
35 + # The bound address should contain our local IP
36 + local_ip = get_ip()
37 + assert (
38 + local_ip in handle.remote_subscribe_addr
39 + or f"[{local_ip}]" in handle.remote_subscribe_addr
40 + )
41 + del mq
42 +
43 +
44 +def test_mq_bind_with_non_local_ip_fails():
45 + """MessageQueue should fail to bind when connect_ip is a
46 + non-local IP address (simulating the bug where master_addr
47 + from a different node was used)."""
48 + # Use a non-local IP that we definitely can't bind to.
49 + # 198.51.100.1 is from TEST-NET-2 (RFC 5737), never locally assigned.
50 + non_local_ip = "198.51.100.1"
51 + with pytest.raises(zmq.error.ZMQError, match="Cannot assign requested address"):
52 + MessageQueue(
53 + n_reader=2,
54 + n_local_reader=1,
55 + connect_ip=non_local_ip,
56 + )
57 +
58 +
59 +def test_mq_bind_defaults_to_local_ip():
60 + """When connect_ip is None, MessageQueue should auto-detect
61 + the local IP and bind successfully."""
62 + mq = MessageQueue(
63 + n_reader=2,
64 + n_local_reader=1,
65 + connect_ip=None, # should fallback to get_ip()
66 + )
67 + handle = mq.export_handle()
68 + assert handle.remote_subscribe_addr is not None
69 + del mq
70 +
71 +
72 +if __name__ == "__main__":
73 + test_mq_bind_with_local_ip()
74 + print("PASSED: test_mq_bind_with_local_ip")
75 + test_mq_bind_with_non_local_ip_fails()
76 + print("PASSED: test_mq_bind_with_non_local_ip_fails")
77 + test_mq_bind_defaults_to_local_ip()
78 + print("PASSED: test_mq_bind_defaults_to_local_ip")
79 + print("\nAll tests passed!")
vllm/v1/executor/multiproc_executor.py
+14 -1
@@ -44,6 +44,7 @@ from vllm.logger import init_logger
44 from vllm.tracing import instrument, maybe_init_worker_tracer
45 from vllm.utils.network_utils import (
46 get_distributed_init_method,
47 + get_ip,
48 get_loopback_ip,
49 get_open_port,
50 )
@@ -128,11 +129,23 @@ class MultiprocExecutor(Executor):
129 # For leader node within each dp rank,
130 # each dp will have its own leader multiproc executor.
131 max_chunk_bytes = envs.VLLM_MQ_MAX_CHUNK_BYTES_MB * 1024 * 1024
132 + mq_connect_ip = get_ip()
133 + logger.info(
134 + "DP group leader: node_rank=%d, node_rank_within_dp=%d, "
135 + "master_addr=%s, mq_connect_ip=%s (local), "
136 + "world_size=%d, local_world_size=%d",
137 + self.parallel_config.node_rank,
138 + self.parallel_config.node_rank_within_dp,
139 + self.parallel_config.master_addr,
140 + mq_connect_ip,
141 + self.world_size,
142 + self.local_world_size,
143 + )
144 self.rpc_broadcast_mq = MessageQueue(
145 self.world_size,
146 self.local_world_size,
147 max_chunk_bytes=max_chunk_bytes,
135 - connect_ip=self.parallel_config.master_addr,
148 + connect_ip=mq_connect_ip,
149 )
150 scheduler_output_handle = self.rpc_broadcast_mq.export_handle()
151 # Create workers