Prhub

#32100 Revert RuntimeContext config-namespace reads/roles (#31813–#31817)

原始 PR 作者 ch-wan 合并时间 2026-07-23 02:52 文件变更 187 提交数 8 评论 6 代码增减 +1432 / -1441

执行摘要

回滚 RuntimeContext 配置命名空间迁移上半部分修复正确性缺陷

PR body 指出合并未经充分验证,存在三个正确性缺陷:

1) declare_load_time_override() 更新 ServerArgs 但构造时读取器仍读 exec.moe 配置 bag(持有启动值),导致 DeepSeek-V2/V4 等模型融合布局不一致;
2) 两个 Engine 实例共进程时 TokenizerManager LoRA 门控读取进程全局上下文而非所属引擎 ServerArgs;
3) speculative_draft_load_format 只写入 bag,draft worker 仍从 server_args.load_format 构建 LoadConfig。

强烈建议阅读 PR body 中列出的三项缺陷及回滚范围,理解配置系统设计中的加载时覆盖与多实例隔离难题。本 PR 是架构决策的宝贵反面案例,值得团队深入讨论。

讨论亮点

自动审查 bot(chatgpt-codex-connector)在三个关键点提出 P1/P2 级别问题:

  • P1 draft build 后配置丢失:调用 set_server_args() 重建 bag 时丢弃已解析的 kv_cache_dtype,导致注意力后端初始化仍看到 "auto"。回滚后 draft build 直接使用 server_args,问题消除。
  • P1 draft runner KV-cache dtype 读取:FlashattentionBackend 从全局 bag 读取 dtype 可能错误使用目标 dtype,回滚后直接使用 model_runner.kv_cache_dtype_str。
  • P2 空槽 restore 泄漏:override_server_args() 的 restore 只清除 _server_args 未清除命名空间访问器缓存,导致后续测试暴露临时配置。回滚后 restore 逻辑简化,泄漏路径关闭。

实现拆解

回滚分四步执行:

  1. 回滚配置命名空间访问器(#31813–#31817):依次 revert 5 个提交,移除 publish_role、命名空间 get_*() 访问器,_ConfigBag 从基于 dict 的 traceable reads 回退为 slots 实现,_set_sub 方法被删除,override 方法改为直接更新字段字典。
  2. 回滚迁移修补提交(#32091、#32096):这两个提交是修复迁移导致的 CI 失败,一并回滚。
  3. 重新应用无关 CI 修复(#32091):该修复独立于配置命名空间迁移,重新 apply 到回滚后的代码上。
  4. 最终代码状态:下半部分(#31809–#31812)保留(字段元数据、配置 bag、只读 ServerArgs、单一审计突变入口),上半部分完全移除。所有 get_() 调用替换为 self.server_args. 直接读取,涉及 187 个文件,核心包括 runtime_context.py、scheduler.py、kv_cache_configurator.py、model_runner.py、eagle_worker_v2.py、tokenizer_manager.py 等。
文件 模块 状态 重要度
python/sglang/srt/runtime_context.py 运行时上下文 modified 8.69
python/sglang/srt/managers/scheduler.py 调度器 modified 7.78
python/sglang/srt/mem_cache/kv_cache_configurator.py 缓存配置器 modified 7.72
python/sglang/srt/model_executor/model_runner.py 模型运行器 modified 7.63
python/sglang/srt/speculative/eagle_worker_v2.py 推测解码 modified 7.44

关键符号

_set_sub preserve_config publish publish_role _apply_adaptive_config

关键源码片段

python/sglang/srt/runtime_context.py core-logic

核心配置命名空间 bag 实现,回滚了 __slots__、_set_sub、publish_role 等关键变更,恢复为基于 __dict__ 的 traceable reads 移除后的版本。

# 回滚后的 _ConfigBag:使用 __slots__ 而非 __dict__,放弃 traceable reads
class _ConfigBag:
    """A resolved-config namespace bag.
    Values are snapshotted from server_args at publish.
    """
​
    __slots__ = ("_path", "_fields", "_subs") # 与 base 版本(无 __slots__)相反
​
    def __init__(self, path: str):
        object.__setattr__(self, "_path", path)
        object.__setattr__(self, "_fields", {})
        object.__setattr__(self, "_subs", {})
​
    def __getattr__(self, name: str) -> Any:
        # 仅在 name 不是 slot 属性时被调用
        fields = object.__getattribute__(self, "_fields")
        if name in fields:
            return fields[name]
        subs = object.__getattribute__(self, "_subs")
        if name in subs:
            return subs[name]
        raise AttributeError(...)
​
    def _set(self, name: str, value: Any) -> None:
        # 回滚后不再设置 real attribute,因此 bag.leaf 不可被 torch.compile 追踪
        object.__getattribute__(self, "_fields")[name] = value
​
    def override(self, **kwargs):
        # 回滚后使用 fields.update,不经过 _set,不维护 real attribute
        fields = object.__getattribute__(self, "_fields")
        unknown = set(kwargs) - set(fields)
        if unknown:
            raise ValueError(...)
        saved = {name: fields[name] for name in kwargs}
        fields.update(kwargs)
        try:
            yield self
        finally:
            fields.update(saved)
python/sglang/srt/managers/scheduler.py dependency-wiring

配置读取从 get_*() 访问器切换回 self.server_args 直接访问,涉及调度器初始化流程中的多项配置。

# 回滚后:导入从 12 个 get_ 函数缩减为 2 个
from sglang.srt.runtime_context import get_context, get_parallel
# 而 base 版本导入:get_context, get_device, get_disagg, get_exec, get_lora,
# get_memory, get_mm, get_observability, get_parallel, get_schedule, get_serving, get_spec# 使用示例:原本 get_observability().enable_metrics -> self.server_args.enable_metrics
enable_metrics=self.server_args.enable_metrics,
# 原本 get_disagg().disaggregation_mode -> self.server_args.disaggregation_mode
self.server_args.disaggregation_mode == "decode"
python/sglang/srt/mem_cache/kv_cache_configurator.py dependency-wiring

KV cache 配置器的配置读取从 get_*() 访问器切换回 server_args,影响内存池和缓存初始化流程。

# 回滚后:导入从 6 个 get_ 函数缩减为 2 个
from sglang.srt.runtime_context import get_model, get_parallel
# base 版本导入:get_disagg, get_exec, get_memory, get_model, get_parallel, get_schedule, get_spec# 示例:原本 get_memory().enable_unified_memory -> self.server_args.enable_unified_memory
if (
    self.server_args.enable_unified_memory
    and self.server_args.disaggregation_mode == "null"
    ...
)

评论区精华

Preserve resolved config when restoring after draft build 正确性

chatgpt-codex-connector 指出在 speculative decode 场景下,调用 set_server_args(saved_server_args) 重建 bag 会丢弃已解析的 kv_cache_dtype,导致注意力后端初始化时仍看到 "auto"。

结论:回滚后该问题不再存在,因为 draft build 流程恢复为直接使用 server_args,不再经过 config bag。 · 已解决

Read the draft runner's own KV-cache dtype 正确性

FlashattentionBackend 从全局 config bag 读取 kv_cache_dtype 可能错误地使用目标模型的 dtype,导致 MXFP8/scaling 行为错误。

结论:回滚后 backend 直接使用 model_runner.kv_cache_dtype_str,避免了 bag 读取。 · 已解决

Clear projected config when restoring an empty slot 正确性

override_server_args() 的 restore 只清除 _server_args 未清除命名空间访问器缓存(如 get_model()),导致后续测试泄漏临时模拟配置。

结论:回滚后 restore 逻辑简化,不再存在此泄漏。 · 已解决

风险与影响

回滚本身风险较低(恢复到已验证代码),但需注意:

  • 下半部分(#31809–#31812)保留的只读 ServerArgs 和 _declarations_materialized 属性与回滚后的代码存在接口兼容性,需确保无未预期的交互。
  • CI 中观察到 3 个失败:1 个由最新提交修复,1 个为 CI 基础设施不稳定,1 个待确认是否由 PR #30924 引入,可能仍有未发现的回归。
  • 回滚后丧失 torch.compile 可追踪配置读取优化,但此优化属于非功能性取舍。

影响范围广泛(187 个文件变更),但用户无直接感知。系统层面所有配置读取路径回退到 server_args 直读,不再经由命名空间 bag 中转。团队需注意后续需修复已识别的同步缺陷后才能重新引入迁移,本回滚为重新设计提供了稳定基点。

多引擎配置泄漏 加载时融合回退不同步 draft load format 被忽略 CI 基础设施不稳定

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论