[PD-Disagg] Fix bootstrap server race condition when prefill workers not yet registered (#19288)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -157,13 +157,26 @@ class CommonKVManager(BaseKVManager):
|
|||||||
with self.failure_lock:
|
with self.failure_lock:
|
||||||
self.failure_records[bootstrap_room] = failure_reason
|
self.failure_records[bootstrap_room] = failure_reason
|
||||||
|
|
||||||
def ensure_parallel_info(self, bootstrap_addr: str) -> bool:
|
def ensure_parallel_info(
|
||||||
|
self, bootstrap_addr: str, max_retries: int = 20, retry_interval: float = 1.0
|
||||||
|
) -> bool:
|
||||||
"""Fetch and cache prefill parallel info if not yet available.
|
"""Fetch and cache prefill parallel info if not yet available.
|
||||||
Returns True if info is available (cached or freshly fetched).
|
Returns True if info is available (cached or freshly fetched).
|
||||||
|
Retries with backoff if the prefill server hasn't registered yet.
|
||||||
"""
|
"""
|
||||||
if bootstrap_addr in self.prefill_info_table:
|
if bootstrap_addr in self.prefill_info_table:
|
||||||
return True
|
return True
|
||||||
info = self._fetch_prefill_server_info(bootstrap_addr)
|
info = None
|
||||||
|
for attempt in range(max_retries):
|
||||||
|
info = self._fetch_prefill_server_info(bootstrap_addr)
|
||||||
|
if info is not None:
|
||||||
|
break
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
logger.info(
|
||||||
|
f"Prefill server info not available from {bootstrap_addr}, "
|
||||||
|
f"retrying ({attempt + 1}/{max_retries})..."
|
||||||
|
)
|
||||||
|
time.sleep(retry_interval)
|
||||||
if info is None:
|
if info is None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -595,6 +608,7 @@ class CommonKVBootstrapServer(BaseKVBootstrapServer):
|
|||||||
int, Dict[int, Dict[int, Dict[str, Union[str, int]]]]
|
int, Dict[int, Dict[int, Dict[str, Union[str, int]]]]
|
||||||
] = {}
|
] = {}
|
||||||
self.room_to_dp_rank: Dict[int, Dict[str, Union[int, float]]] = {}
|
self.room_to_dp_rank: Dict[int, Dict[str, Union[int, float]]] = {}
|
||||||
|
self._registered_count = 0
|
||||||
self.entry_cleanup_interval = (
|
self.entry_cleanup_interval = (
|
||||||
envs.SGLANG_DISAGGREGATION_BOOTSTRAP_ENTRY_CLEANUP_INTERVAL.get()
|
envs.SGLANG_DISAGGREGATION_BOOTSTRAP_ENTRY_CLEANUP_INTERVAL.get()
|
||||||
)
|
)
|
||||||
@@ -606,6 +620,14 @@ class CommonKVBootstrapServer(BaseKVBootstrapServer):
|
|||||||
def run(self):
|
def run(self):
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
|
def _is_ready(self) -> bool:
|
||||||
|
if self.attn_tp_size is None or self.pp_size is None:
|
||||||
|
return False
|
||||||
|
# TODO: verify this expected count is correct for all parallelism
|
||||||
|
# combinations (CP / DP attention / system DP / TP / PP).
|
||||||
|
expected = self.dp_size * self.attn_tp_size * self.pp_size
|
||||||
|
return self._registered_count >= expected
|
||||||
|
|
||||||
def _setup_routes(self):
|
def _setup_routes(self):
|
||||||
self.app.router.add_route("*", "/route", self._handle_route)
|
self.app.router.add_route("*", "/route", self._handle_route)
|
||||||
self.app.router.add_post("/register_dp_rank", self._handle_register_dp_rank)
|
self.app.router.add_post("/register_dp_rank", self._handle_register_dp_rank)
|
||||||
@@ -676,8 +698,11 @@ class CommonKVBootstrapServer(BaseKVBootstrapServer):
|
|||||||
"rank_ip": rank_ip,
|
"rank_ip": rank_ip,
|
||||||
"rank_port": rank_port,
|
"rank_port": rank_port,
|
||||||
}
|
}
|
||||||
|
self._registered_count += 1
|
||||||
|
expected = self.dp_size * self.attn_tp_size * self.pp_size
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"Register prefill bootstrap: DP{dp_group} TP{attn_tp_rank} PP{pp_rank} with rank_ip: {rank_ip} and rank_port: {rank_port}"
|
f"Register prefill bootstrap: DP{dp_group} TP{attn_tp_rank} PP{pp_rank} with rank_ip: {rank_ip} and rank_port: {rank_port}"
|
||||||
|
f" ({self._registered_count}/{expected} registered)"
|
||||||
)
|
)
|
||||||
|
|
||||||
return web.Response(text="OK", status=200)
|
return web.Response(text="OK", status=200)
|
||||||
@@ -694,6 +719,12 @@ class CommonKVBootstrapServer(BaseKVBootstrapServer):
|
|||||||
and int(prefill_dp_rank) == -1
|
and int(prefill_dp_rank) == -1
|
||||||
and int(target_pp_rank) == -1
|
and int(target_pp_rank) == -1
|
||||||
):
|
):
|
||||||
|
if not self._is_ready():
|
||||||
|
return web.Response(
|
||||||
|
text=f"Prefill server not fully registered yet"
|
||||||
|
f" ({self._registered_count} workers registered).",
|
||||||
|
status=503,
|
||||||
|
)
|
||||||
info = PrefillServerInfo(
|
info = PrefillServerInfo(
|
||||||
attn_tp_size=self.attn_tp_size,
|
attn_tp_size=self.attn_tp_size,
|
||||||
dp_size=self.dp_size,
|
dp_size=self.dp_size,
|
||||||
@@ -707,16 +738,27 @@ class CommonKVBootstrapServer(BaseKVBootstrapServer):
|
|||||||
)
|
)
|
||||||
return web.json_response(dataclasses.asdict(info), status=200)
|
return web.json_response(dataclasses.asdict(info), status=200)
|
||||||
|
|
||||||
# Find corresponding prefill info
|
if not self._is_ready():
|
||||||
async with self.lock:
|
return web.Response(
|
||||||
bootstrap_info = self.prefill_port_table[int(prefill_dp_rank)][
|
text=f"Prefill server not fully registered yet"
|
||||||
int(engine_rank)
|
f" ({self._registered_count} workers registered).",
|
||||||
][int(target_pp_rank)]
|
status=503,
|
||||||
|
)
|
||||||
|
|
||||||
if bootstrap_info is not None:
|
# Find corresponding prefill info
|
||||||
return web.json_response(bootstrap_info, status=200)
|
try:
|
||||||
else:
|
async with self.lock:
|
||||||
return web.Response(text="Bootstrap info not Found", status=404)
|
bootstrap_info = self.prefill_port_table[int(prefill_dp_rank)][
|
||||||
|
int(engine_rank)
|
||||||
|
][int(target_pp_rank)]
|
||||||
|
except KeyError:
|
||||||
|
return web.Response(
|
||||||
|
text=f"Bootstrap info not found for dp_rank={prefill_dp_rank} "
|
||||||
|
f"engine_rank={engine_rank} pp_rank={target_pp_rank}",
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(bootstrap_info, status=200)
|
||||||
|
|
||||||
async def _handle_register_dp_rank(self, request: web.Request):
|
async def _handle_register_dp_rank(self, request: web.Request):
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
|
|||||||
Reference in New Issue
Block a user