Skip to content

Conversation

@liyonghua0910
Copy link
Collaborator

@liyonghua0910 liyonghua0910 commented Dec 6, 2025

Motivation

该 PR 旨在实现两个目标:

  1. 简化 PD 分离的部署流程和参数,包括端口号配置、RDMA 网卡检测、相关环境变量设置等等工序,实现【启动 Router】→【启动 P&D 实例】→【部署完成】的简易部署流程。其中启动参数的简化也期望适用于集中式部署和多 TP/DP 部署,并兼容通过 APIServer 和 MultiAPIServer 多 DP 服务的启动方式。
  2. 重构当前代码中与端口号相关的配置处理和使用逻辑。在参数初始化时,若用户未指定端口号,自动寻找可用端口,需要支持在线服务和离线接口;在多 DP 部署场景,在配置初始化时切分好各 DP 所需的端口号,而不是在使用时才临时切分。尽量实现配置的静态化、只读化,减少运行时的配置更改。

Modifications

Usage or Command

Accuracy Tests

Checklist

  • Add at least a tag in the PR title.
    • Tag list: [[FDConfig],[APIServer],[Engine], [Scheduler], [PD Disaggregation], [Executor], [Graph Optimization], [Speculative Decoding], [RL], [Models], [Quantization], [Loader], [OP], [KVCache], [DataProcessor], [BugFix], [Docs], [CI], [Optimization], [Feature], [Benchmark], [Others], [XPU], [HPU], [GCU], [DCU], [Iluvatar], [Metax]]
    • You can add new tags based on the PR content, but the semantics must be clear.
  • Format your code, run pre-commit before commit.
  • Add unit tests. Please write the reason in this PR if no unit tests.
  • Provide accuracy results.
  • If the current PR is submitting to the release branch, make sure the PR has been submitted to the develop branch, then cherry-pick it to the release branch with the [Cherry-Pick] PR tag.

@paddle-bot
Copy link

paddle-bot bot commented Dec 6, 2025

Thanks for your contribution!

@codecov-commenter
Copy link

codecov-commenter commented Dec 6, 2025

Codecov Report

❌ Patch coverage is 52.66272% with 80 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop@83ea964). Learn more about missing BASE report.

Files with missing lines Patch % Lines
fastdeploy/entrypoints/openai/multi_api_server.py 36.36% 24 Missing and 11 partials ⚠️
...he_manager/transfer_factory/rdma_cache_transfer.py 3.84% 25 Missing ⚠️
fastdeploy/utils.py 61.29% 5 Missing and 7 partials ⚠️
fastdeploy/engine/args_utils.py 92.30% 1 Missing and 1 partial ⚠️
fastdeploy/engine/common_engine.py 71.42% 2 Missing ⚠️
fastdeploy/engine/engine.py 33.33% 1 Missing and 1 partial ⚠️
fastdeploy/worker/worker_process.py 33.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #5415   +/-   ##
==========================================
  Coverage           ?   58.04%           
==========================================
  Files              ?      329           
  Lines              ?    41002           
  Branches           ?     6206           
==========================================
  Hits               ?    23798           
  Misses             ?    15379           
  Partials           ?     1825           
Flag Coverage Δ
GPU 58.04% <52.66%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors port configuration for PD-disaggregated deployments by introducing automatic port allocation and consolidating port management logic. The changes aim to simplify configuration by removing the need for users to manually specify multiple ports per service.

Key Changes

  • Introduced automatic port discovery via find_free_ports() function with validation in post_init_all_ports()
  • Centralized port extraction logic in FDConfig.postprocess() to set per-DP ports (local_engine_worker_queue_port, etc.)
  • Added RDMA environment auto-detection via new get_rdma_nics.sh script

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
fastdeploy/utils.py Added parse_ports(), find_free_ports(), and modified is_port_available() for port management
fastdeploy/engine/args_utils.py Added post_init_all_ports() to automatically allocate ports; changed default cache_transfer_protocol to "ipc,rdma"
fastdeploy/config.py Moved port extraction logic to postprocess() method; added local_engine_worker_queue_port attribute
fastdeploy/worker/worker_process.py Moved port assignment from pre-config to post-config initialization
fastdeploy/engine/engine.py Updated to use local_engine_worker_queue_port for DP queue services
fastdeploy/engine/common_engine.py Simplified port handling by using local_engine_worker_queue_port directly
fastdeploy/engine/expert_service.py Removed local port/device ID extraction logic (now handled in config)
fastdeploy/splitwise/splitwise_connector.py Changed to use scalar pd_comm_port and local_device_ids
fastdeploy/cache_manager/prefix_cache_manager.py Renamed pid_suffix to ipc_suffix; updated RDMA port usage
fastdeploy/cache_manager/cache_transfer_manager.py Renamed engine_pid to ipc_suffix for clarity
fastdeploy/cache_manager/cache_messager.py Renamed engine_pid to ipc_suffix for consistency
fastdeploy/cache_manager/transfer_factory/rdma_cache_transfer.py Added automatic RDMA environment setup and NIC detection
fastdeploy/cache_manager/transfer_factory/get_rdma_nics.sh New script for detecting RDMA-capable network interfaces
examples/splitwise/utils.sh Improved health check display with UDP port checking
examples/splitwise/start_v1_tp1.sh Simplified example by removing manual port specifications

+ f" --engine_pid {pid_suffix}"
+ f" --rdma_port {cache_config.rdma_comm_ports[i] if cache_config.rdma_comm_ports is not None else '0'}"
+ f" --ipc_suffix {ipc_suffix}"
+ f" --rdma_port {cache_config.rdma_comm_ports[0] if cache_config.rdma_comm_ports is not None else '0'}"
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to line 280, using only rdma_comm_ports[0] for all tensor parallel ranks is incorrect. Each rank needs its corresponding RDMA port:

+ f" --rdma_port {cache_config.rdma_comm_ports[i] if cache_config.rdma_comm_ports is not None else '0'}"
Suggested change
+ f" --rdma_port {cache_config.rdma_comm_ports[0] if cache_config.rdma_comm_ports is not None else '0'}"
+ f" --rdma_port {cache_config.rdma_comm_ports[i] if cache_config.rdma_comm_ports is not None else '0'}"

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修复

Comment on lines +612 to +614
return [int(p) for p in ports.split(",")]
elif isinstance(ports, list):
return [int(p) for p in ports]
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parse_ports function doesn't handle invalid input gracefully. If the string contains non-numeric values or the list contains non-convertible elements, it will raise a ValueError without a clear error message. Consider adding try-except with a more descriptive error:

elif isinstance(ports, str):
    try:
        return [int(p.strip()) for p in ports.split(",")]
    except ValueError as e:
        raise TypeError(f"Cannot parse ports string '{ports}': contains non-numeric values") from e
Suggested change
return [int(p) for p in ports.split(",")]
elif isinstance(ports, list):
return [int(p) for p in ports]
try:
return [int(p.strip()) for p in ports.split(",")]
except (ValueError, TypeError) as e:
raise TypeError(f"Cannot parse ports string '{ports}': contains non-numeric values") from e
elif isinstance(ports, list):
try:
return [int(p) for p in ports]
except (ValueError, TypeError) as e:
raise TypeError(f"Cannot parse ports list '{ports}': contains non-numeric values") from e

Copilot uses AI. Check for mistakes.

try:
import rdma_comm
except:
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a bare except: clause catches all exceptions including SystemExit and KeyboardInterrupt, which can make the program difficult to debug or terminate properly. Use except Exception: or a more specific exception type instead:

except ImportError:
    raise RuntimeError(
        "The installation of the RDMA library failed. Confirm whether your network card supports RDMA transmission."
    )
Suggested change
except:
except ImportError:

Copilot uses AI. Check for mistakes.
"""

cache_transfer_protocol: str = "ipc"
cache_transfer_protocol: str = "ipc,rdma"
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for cache_transfer_protocol changed from "ipc" to "ipc,rdma". This is a breaking change because it now requires RDMA to be configured even if users only want IPC. Consider keeping the default as "ipc" for backward compatibility, or document this breaking change clearly.

Suggested change
cache_transfer_protocol: str = "ipc,rdma"
cache_transfer_protocol: str = "ipc"

Copilot uses AI. Check for mistakes.
Comment on lines 549 to 552
assert (
len(ports) == num_total_ports
), f"Parameter `{name}` should have {num_total_ports} ports, got {len(ports)}."
ports = parse_ports(ports)
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic error in port validation. At line 552, parse_ports(ports) is called after the assertion at line 549-551, but the assertion checks len(ports) before parsing. If ports is an int or str (valid inputs), the len() call will fail or give incorrect results.

The parse_ports() call should happen before the length assertion:

ports = parse_ports(ports)
if ports is not None:
    assert (
        len(ports) == num_total_ports
    ), f"Parameter `{name}` should have {num_total_ports} ports, got {len(ports)}."
Suggested change
assert (
len(ports) == num_total_ports
), f"Parameter `{name}` should have {num_total_ports} ports, got {len(ports)}."
ports = parse_ports(ports)
ports = parse_ports(ports)
assert (
len(ports) == num_total_ports
), f"Parameter `{name}` should have {num_total_ports} ports, got {len(ports)}."

Copilot uses AI. Check for mistakes.
Comment on lines +677 to +678
if is_port_available(host, port):
free_ports.append(port)
Copy link

Copilot AI Dec 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The find_free_ports function relies on is_port_available, which has a critical bug (always returns True). This means find_free_ports will incorrectly consider all ports as available and may attempt to use ports that are already in use, leading to runtime failures when the ports are actually bound.

Copilot uses AI. Check for mistakes.
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