Prhub

#24767 [Utils] Make request dump robust to unpicklable server_args and large meta_info

原始 PR 作者 ByronHsu 合并时间 2026-05-10 12:41 文件变更 3 提交数 2 评论 5 代码增减 +71 / -11

执行摘要

增强请求转储的鲁棒性与可配置性

PR body 明确指出:在 --trust-remote-code + MoE 模型下,ServerArgs.get_model_config() 懒加载的 ModelConfig 中的 hf_config 处于动态命名空间 transformers_modules.* 下,无法安全 Pickle,导致 _dump_data_to_filedump_requests_before_crash 留下空/损坏的 .pkl 文件。同时,--enable-routing-replay--return-hidden-states 会在 meta_info 中注入 base64 编码的 routed_expertshidden_states,这些数据在回放工具中并不使用。

建议精读 tokenizer_manager.py 中的 dump_requests_dump_data_to_filedump_requests_before_crash 方法,理解 Pickle 回退的设计模式。值得关注的设计决策是:在出错时只丢弃 server_args 而非整体放弃,这是一种优雅的降级方案。

讨论亮点

Review 中 ispobock 提出了关于注释风格的改进建议,建议遵循“默认无注释,仅对非显而易见的 WHY 写注释”的原则。ByronHsu 表示赞同,并在第二个 commit 中精简了原有过多的描述性注释,仅保留了关于 pickle 回退原因的说明。

实现拆解

  1. Pickle 安全回退:在 tokenizer_manager.py_dump_data_to_filedump_requests_before_crash 两个方法中,将 pickle.dump 包裹在 try/except 中;捕获 Exception 后,将 server_args 字段置为 None 重新尝试序列化,确保请求数据至少能保留。
  2. meta_info 键过滤:在 TokenizerManagerinit_request_logging_and_dumping 中新增实例属性 dump_requests_exclude_meta_keys,默认值为 ['routed_experts', 'hidden_states']。在 dump_requests 方法中,若 meta_info 包含这些键,则创建浅拷贝副本去除它们,不修改原 out_dict
  3. 数据契约扩展:在 io_struct.pyConfigureLoggingReq dataclass 中新增可选字段 dump_requests_exclude_meta_keys: Optional[List[str]],使得可通过 API 动态配置。
  4. CLI 入口更新:在 configure_logging.py 中新增 --dump-requests-exclude-meta-keys 参数,支持以逗号分隔的字符串输入,空字符串表示保留所有键;若未设置则使用服务端默认值。
文件 模块 状态 重要度
python/sglang/srt/managers/tokenizer_manager.py 管理器 modified 6.89
python/sglang/srt/managers/configure_logging.py CLI 工具 modified 6.45
python/sglang/srt/managers/io_struct.py 数据结构 modified 4.58

关键符号

TokenizerManager.dump_requests TokenizerManager._dump_data_to_file TokenizerManager.dump_requests_before_crash TokenizerManager.configure_logging

关键源码片段

python/sglang/srt/managers/tokenizer_manager.py core-logic

核心改动所在:修改了转储相关的三个方法,实现了 Pickle 安全回退和 meta_info 键过滤。

# python/sglang/srt/managers/tokenizer_manager.py (partial)def dump_requests(self, state: ReqState, out_dict: dict):
    # 如果配置了排除键且 meta_info 是 dict,则创建过滤后的副本
    if self.dump_requests_exclude_meta_keys and isinstance(
        out_dict.get("meta_info"), dict
    ):
        exclude = self.dump_requests_exclude_meta_keys
        if any(k in out_dict["meta_info"] for k in exclude):
            filtered_meta = {
                k: v
                for k, v in out_dict["meta_info"].items()
                if k not in exclude
            }
            # 不修改原 out_dict,仅对副本做过滤
            out_dict = {**out_dict, "meta_info": filtered_meta}
​
    self.dump_request_list.append(
        (state.obj, time.time(), out_dict, state.rid)
    )
    if len(self.dump_request_list) >= self.dump_requests_threshold:
        self._dump_data_to_file()
​
​
def _dump_data_to_file(self, ...):
    ...
    def background_task():
        os.makedirs(os.path.dirname(filename), exist_ok=True)
        with open(filename, "wb") as f:
            try:
                pickle.dump(to_dump_with_server_args, f)
            except Exception as e:
                # ServerArgs 在 --trust-remote-code 下可能因动态
                # transformers_modules 命名空间而不可 pickle。
                # 降级:丢弃 server_args 仅保留请求数据。
                logger.error(
                    f"Failed to pickle dump with server_args: {e!r}; "
                    "retrying without server_args"
                )
                f.seek(0)
                f.truncate()
                to_dump_with_server_args["server_args"] = None
                pickle.dump(to_dump_with_server_args, f)
​
    asyncio.create_task(asyncio.to_thread(background_task))
​
​
def dump_requests_before_crash(self, ...):
    ...
    with open(filename, "wb") as f:
        try:
            pickle.dump(data_to_dump_with_server_args, f)
        except Exception as e:
            # 同上,降级策略一致
            logger.error(...)
            f.seek(0)
            f.truncate()
            data_to_dump_with_server_args["server_args"] = None
            pickle.dump(data_to_dump_with_server_args, f)
python/sglang/srt/managers/configure_logging.py core-logic

CLI 入口,新增 --dump-requests-exclude-meta-keys 参数,支持逗号分隔字符串。

# python/sglang/srt/managers/configure_logging.py (partial)if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--url", type=str, default="http://localhost:30000")
    parser.add_argument("--log-requests", action="store_true")
    parser.add_argument("--log-requests-level", type=int, default=3)
    parser.add_argument("--dump-requests-folder", type=str, default="/tmp/sglang_request_dump")
    parser.add_argument("--dump-requests-threshold", type=int, default=1000)
    parser.add_argument(
        "--dump-requests-exclude-meta-keys",
        type=str,
        default=None,
        help=(
            "Comma-separated meta_info keys to strip from each dumped request "
            "(e.g. 'routed_experts,hidden_states'). Pass an empty string to "
            "keep all keys. If not set, the server default is used."
        ),
    )
    args = parser.parse_args()
​
    payload = {
        "log_requests": args.log_requests,
        "log_requests_level": args.log_requests_level,
        "dump_requests_folder": args.dump_requests_folder,
        "dump_requests_threshold": args.dump_requests_threshold,
    }
    # 仅当用户显式指定时才覆盖服务端默认值
    if args.dump_requests_exclude_meta_keys is not None:
        payload["dump_requests_exclude_meta_keys"] = [
            k.strip()
            for k in args.dump_requests_exclude_meta_keys.split(",")
            if k.strip()
        ]
​
    response = requests.post(args.url + "/configure_logging", json=payload)
    assert response.status_code == 200

评论区精华

注释风格优化 style

ispobock 建议精简注释,仅保留非显而易见的 WHY 注释;引用其原则:'Default: no comment. Good names and short functions document themselves.'

结论:ByronHsu 接受建议,在第二个 commit 中移除了过量的描述性注释。 · 已解决

风险与影响

本次变更主要新增了 try/except 路径和可配置的过滤逻辑,整体风险较低。但需要注意:

  • 如果 out_dictmeta_info 字段不是 dict 类型(例如 None 或其他类型),isinstance 检查会安全跳过,不会抛出异常。
  • 过滤后使用 {**out_dict, 'meta_info': filtered_meta} 创建新字典,对于超大 out_dict 可能存在短暂的额外内存开销。
  • 无直接测试覆盖新增的逻辑路径(try/except 和过滤分支),建议后续增加单元测试。

用户:对于使用 --trust-remote-code 和 MoE 模型的用户,请求转储不再因 Pickle 错误而丢失数据;同时转储文件体积显著减小。用户可以通过新的 CLI 参数或 API 字段自定义需要排除的 meta_info 键。
系统:无性能影响,仅在转储时引入额外的 try/except 和字典复制操作。
团队:新增了一个小的数据契约字段和 CLI 参数,维护成本低。

缺少测试覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论