# PR #34080 完整报告

- 仓库：`sgl-project/sglang`
- 标题：config: retire the hidden global fallbacks and the mamba-extra-buffer instance reads
- 合并时间：2026-08-10 05:43
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/34080

---

# 执行摘要

- 一句话：移除隐藏全局配置回退，mamba 谓词改读配置袋
- 推荐动作：值得精读，尤其是关注配置架构的工程师。本 PR 展示了三类可迁移的方法论：(1) 用 read ratchet 制度识别“可选注入”反模式，并让谓词归属到真正拥有数据的配置袋；(2) 通过 AST 扫描死绑定来做迁移清理的机械归因，避免靠肉眼遗留死代码；(3) 在 post-publish 与 mid-resolution 之间维持谓词唯一定义的双轨约定。对 mamba radix cache 或 DeepSeek MoE 双流图路径有维护需求的同学，建议顺带阅读 review 中关于测试 fixture 迁移的讨论。

# 功能与动机

PR body 明确指出两类“看起来像别的、实际在读进程配置”的形态：optional-injection 惯用法 `f(server_args=None)` 后跟 `server_args = get_server_args()`，在“已注入实例”的外表下悄悄读进程配置，导致 read ratchet（按参数名判定调用者决策的机制）永远看不到这些读取；而 `enable_mamba_extra_buffer` / `_lazy` 本是两个已发布叶子（`memory.disable_radix_cache`、`exec.mamba.mamba_radix_cache_strategy`）的纯函数，十二个调用点没有理由绕道 startup record。作者自评中也强调这是“reader convergence”的一部分，最终让业务代码不再直读发布后的 ServerArgs。

# 实现拆解

### 1. 移除 optional-injection 死参数
- `python/sglang/srt/speculative/spec_utils.py`：`spec_need_hidden_states(server_args=None)` 的唯一定位调用者从不传参，去掉参数改为读 `get_spec().speculative_algorithm` / `get_spec().enable_multi_layer_eagle`。
- `python/sglang/srt/models/deepseek_v2.py`：`_can_dual_stream_graph(hidden_states, server_args=None)` 中 `enable_eplb` 改读 `get_exec().moe.enable_eplb`，谓词不再需要 config 参数，`forward` 中的调用同步去掉绑定。
- `python/sglang/srt/mem_cache/allocation_sizing.py`：`get_alloc_len_per_decode` 的两个调用者均显式传 config，参数改为必填；模块唯一的“which config”决策点收敛到 `get_alloc_reserve_per_decode`。

### 2. 新增 post-publish 配置袋谓词
- `python/sglang/srt/runtime_context.py`：新增 `mamba_extra_buffer_enabled()` 与 `mamba_extra_buffer_lazy_enabled()`，直接读 `get_memory().disable_radix_cache` 与 `get_exec().mamba.mamba_radix_cache_strategy` 两个已发布叶子。
- `python/sglang/srt/arg_groups/overrides.py`：补全 `mamba_extra_buffer_lazy_of` 作为 lazy 变体，并明确 `mamba_extra_buffer_of` 是谓词的唯一定义：`ServerArgs` 成员委托它，`runtime_context` 访问器是其 post-publish 兄弟（因两个叶子落在不同 bag 无法直接复用）。

### 3. 十二处调用点迁移与死绑定清理
- `schedule_batch.py` 的 `prepare_for_extend` / `prepare_for_decode`、`spec_utils.py` 的 `prepare_mamba_track_for_verify` / `spec_prepare_for_decode`、`batch_result_processor.py` 的 `_handle_finish_state_updated_req` / `_mamba_prefix_cache_update`、`inkling.py` 的 `__init__`、`sconv.py` 的 `_update_sconv_cache_for_draft_extend` 全部改用袋谓词。
- 顺带删除因迁移而失效的 `server_args = get_server_args()` 死绑定（AST 扫描确认本 PR 负责 4 处：`prepare_for_extend`、`prepare_for_decode`、`prepare_mamba_track_for_verify`、`Inkling.__init__`），其余 5 处留给 #34081。

### 4. 测试配套：从 fake getter 转为 publish 配置
- `test_batch_result_processor_mamba_boundary.py`：改用 `get_context().override_server_args(mamba_radix_cache_strategy="extra_buffer", mamba_track_interval=4)` 发布配置，删除对 `get_server_args` / `get_exec` / `get_observability` / `get_disagg` 的冗余 patch。
- `test_schedule_batch_prepare_for_decode.py`：改为发布 `override_server_args`，避免 `config namespace 'memory' not published`。
- `test_attention_patching.py（MLX）`：改为 patch 新谓词 `batch_result_processor.mamba_extra_buffer_lazy_enabled`。

### 5. 收敛度量
- 全局配置读取计数从 45 降到 24；ratchet 与 runtime-context / server-args / models / spec 套件在 PR 边界全部通过（单独跑 225 passed / 38 skipped）。

关键文件：
- `python/sglang/srt/runtime_context.py`（模块 配置层；类别 source；类型 core-logic；符号 mamba_extra_buffer_enabled, mamba_extra_buffer_lazy_enabled）: 新增 `mamba_extra_buffer_enabled` / `mamba_extra_buffer_lazy_enabled` 两个配置袋谓词，是十二处调用点迁移的锚点，定义了 post-publish 的读取语义。
- `python/sglang/srt/speculative/spec_utils.py`（模块 投机解码；类别 source；类型 core-logic；符号 spec_need_hidden_states, prepare_mamba_track_for_verify, spec_prepare_for_decode）: 代表性文件：`spec_need_hidden_states` 移除 optional-injection 死参数并改读 `get_spec()`；`prepare_mamba_track_for_verify` / `spec_prepare_for_decode` 迁移到袋谓词并删除死绑定。
- `python/sglang/srt/mem_cache/allocation_sizing.py`（模块 显存分配；类别 source；类型 data-contract；符号 get_alloc_len_per_decode, get_alloc_reserve_per_decode）: `get_alloc_len_per_decode` 参数从可选变为必填，模块级“哪个 config”决策点唯一收敛到 `get_alloc_reserve_per_decode`，是契约收紧的典型示范。
- `python/sglang/srt/managers/schedule_batch.py`（模块 调度器；类别 source；类型 dependency-wiring；符号 prepare_for_extend, prepare_for_decode）: 调度热路径 `prepare_for_extend` / `prepare_for_decode` 改用袋谓词，并删除两处死绑定；是本次迁移影响面最大的调用方。
- `python/sglang/srt/models/deepseek_v2.py`（模块 模型层；类别 source；类型 data-contract；符号 _can_dual_stream_graph）: `_can_dual_stream_graph` 移除 `server_args=None` 参数，`enable_eplb` 改读 `get_exec().moe.enable_eplb`，影响 DeepSeek-V2 双流图决策。
- `python/sglang/srt/arg_groups/overrides.py`（模块 覆盖配置；类别 source；类型 core-logic；符号 mamba_extra_buffer_lazy_of）: 新增 `mamba_extra_buffer_lazy_of`，并明确 `mamba_extra_buffer_of` 是谓词的 mid-resolution 唯一定义。
- `python/sglang/srt/managers/scheduler_components/batch_result_processor.py`（模块 批处理；类别 source；类型 dependency-wiring；符号 _handle_finish_state_updated_req, _mamba_prefix_cache_update）: `_handle_finish_state_updated_req` 与 `_mamba_prefix_cache_update` 两处释放 / 缓存更新路径改用袋谓词，并曾在 review 中被指测试破坏。
- `python/sglang/srt/models/inkling_common/sconv.py`（模块 卷积层；类别 source；类型 dependency-wiring；符号 _update_sconv_cache_for_draft_extend）: `_update_sconv_cache_for_draft_extend` 中 do_tracking 判断改用 `mamba_extra_buffer_enabled()`，去掉 getter 依赖。
- `python/sglang/srt/models/inkling.py`（模块 模型层；类别 source；类型 data-contract；符号 Inkling.__init__）: 初始化断言从 `server_args.enable_mamba_extra_buffer()` 改为 `mamba_extra_buffer_enabled()`，并移除对应死绑定与 import。
- `test/registered/unit/managers/test_batch_result_processor_mamba_boundary.py`（模块 测试；类别 test；类型 test-coverage；符号 test_overlap_scheduler_handles_zero_and_one_batch_lookahead）: 从 fake `get_server_args` 双体改为 `override_server_args` 发布配置，是 codex 指出的测试破坏修复之一。
- `test/registered/unit/managers/test_schedule_batch_prepare_for_decode.py`（模块 测试；类别 test；类型 test-coverage）: 单独运行时曾因 bag 未发布崩溃，改为 `override_server_args` 发布配置，保证本 PR 可独立合入。
- `test/registered/unit/hardware_backend/mlx/test_attention_patching.py`（模块 测试；类别 test；类型 test-coverage；符号 test_finished_request_snapshots_before_release）: MLX 环境的 finished-request 测试改为 patch 新谓词 `mamba_extra_buffer_lazy_enabled`，避免依赖其他用例先发布配置。

关键符号：spec_need_hidden_states, _can_dual_stream_graph, mamba_extra_buffer_enabled, mamba_extra_buffer_lazy_enabled, get_alloc_len_per_decode, get_alloc_reserve_per_decode, mamba_extra_buffer_lazy_of, prepare_mamba_track_for_verify, _handle_finish_state_updated_req, _mamba_prefix_cache_update, _update_sconv_cache_for_draft_extend

## 关键源码片段

### `python/sglang/srt/speculative/spec_utils.py`

代表性文件：`spec_need_hidden_states` 移除 optional-injection 死参数并改读 `get_spec()`；`prepare_mamba_track_for_verify` / `spec_prepare_for_decode` 迁移到袋谓词并删除死绑定。

```python
def spec_need_hidden_states() -> bool:
    # STANDALONE 草稿不消费 `spec_info.hidden_states`（vanilla LLM）。
    # multi_layer_eagle、DFLASH、DSPARK 不通过 FutureMap 转发 hidden_states。
    # TODO(lsyin): 当 step == 1 时也可以跳过。
    spec = get_spec()
    if spec.speculative_algorithm in ("STANDALONE", "DFLASH", "DSPARK"):
        return False
    return not spec.enable_multi_layer_eagle


def prepare_mamba_track_for_verify(batch: ScheduleBatch) -> None:
    """在 TARGET_VERIFY forward 前从 reqs 重建 mamba track 索引。

    投机批次会跳过 prepare_for_decode 中的刷新，且 filter/merge 会使这些
    字段置空，所以必须在 verify 前重建；同时清掉 mask，避免 extend 阶段的
    旧 mask 在 TARGET_VERIFY 中触发 in-forward 跟踪（跟踪改由
    commit_mamba_states_after_verify 完成）。

    lazy 模式：收集 `mamba_lazy_spec_prepare` 规划的 track 位置；本函数运行
    在 forward 隔离区，因此不得修改 req/pool 状态。
    """
    if not mamba_extra_buffer_enabled():
        return
    track_positions = None
    if mamba_extra_buffer_lazy_enabled():
        track_positions = batch.mamba_lazy_spec_track_positions_cpu
        assert track_positions is not None and len(track_positions) == len(
            batch.reqs
        ), (
            "lazy spec verify without a track plan: mamba_lazy_spec_prepare "
            "must run in prepare_for_decode for every spec decode iteration"
        )
    set_mamba_track_indices_from_reqs(batch, track_positions)
    batch.mamba_track_mask = None
    batch.mamba_track_seqlens = None

```

### `python/sglang/srt/mem_cache/allocation_sizing.py`

`get_alloc_len_per_decode` 参数从可选变为必填，模块级“哪个 config”决策点唯一收敛到 `get_alloc_reserve_per_decode`，是契约收紧的典型示范。

```python
def get_alloc_len_per_decode(server_args: ServerArgs) -> int:
    # 参数现在是必填的：两个调用者本来就传入 config，模块内部的全局
    # 回退是死代码，移除后调用契约更清晰。
    if server_args.speculative_algorithm is None:
        return 1

    # 投机解码按 max(topk * num_steps, num_draft_tokens) 预留每步 decode
    # 的 KV 长度。
    spec_steps = server_args.speculative_num_steps or 1
    spec_topk = server_args.speculative_eagle_topk or 1
    spec_tokens = server_args.max_speculative_num_draft_tokens
    page_size = server_args.page_size

    from sglang.srt.speculative.spec_info import SpeculativeAlgorithm

    spec_algo = SpeculativeAlgorithm.from_string(server_args.speculative_algorithm)
    if page_size == 1 or spec_topk == 1 or not spec_algo.has_draft_kv():
        return max(spec_steps * spec_topk, spec_tokens)
    else:
        # spec v2 树形结构（page>1, topk>1）：每个 topk 分支按页对齐的
        # 最坏占用为 ceil((page_size-1 + num_steps) / page) 页，且各分支
        # 相互复制，因此要为所有 topk 分支统一预留。
        num_new_pages_per_topk = (
            (page_size - 1) + spec_steps + page_size - 1
        ) // page_size
        return max(num_new_pages_per_topk * page_size * spec_topk, spec_tokens)


def get_alloc_reserve_per_decode(server_args: Optional[ServerArgs] = None) -> int:
    """每个请求在每个 decode 步预留的 KV 长度。

    2x 是一个双缓冲，用于吸收 overlap 模式下 kv_committed_len 的滞后；
    请求路径上的调用者手中没有 config，因此本函数保留可选的全局读取，
    成为本模块唯一的“哪个 config”决策点，其下所有函数都显式传实例。
    """
    if server_args is None:
        server_args = get_server_args()
    return 2 * get_alloc_len_per_decode(server_args)

```

# 评论区精华

本 PR 的 17 条 review 评论主要来自 ch-wan 的 stack 自评（基于 review vehicle #34087）与 codex 机器审阅，核心交锋如下：

- codex 连续三次指出“袋谓词绕过旧 mock”导致单测隔离运行时崩溃：`prepare_for_decode` 的 `mamba_extra_buffer_enabled()` 会让 `test_schedule_batch_prepare_for_decode.py` 抛 `ValueError: config namespace 'memory' not published`。ch-wan 逐条核对后确认 MLX 与 boundary 两个用例真实失败并修复，而 schedule-batch 用例在 main 上已被 `override_server_args` 转换、不重现。
- ch-wan 自评将 `test_schedule_batch_prepare_for_decode` 的失败标记为“单独合入的 blocker”，要求本 PR 折入修复或紧随合入 #34081；最终修复折入本 PR，独立 green。
- 死绑定清理从“4 处”扩展为“AST 扫描 9 处”：ch-wan 用 AST 遍历包内 `server_args = get_server_args()` 且后续无 `Load` 的代码，准确定位本 PR 应删 4 处、留给 #34081 5 处，并顺带移除测试中冗余的 getter patch。
- `overrides.py` 的注释确立了“谓词唯一定义”原则：mid-resolution 的 `mamba_extra_buffer_of` 与 post-publish 的 `runtime_context.mamba_extra_buffer_enabled` 是同一谓词在不同解析阶段的双轨实现，必须保持字段路径一致。

- schedule_batch prepare_for_decode 测试隔离运行崩溃 (testing): ch-wan 回复不重现：该测试在 main 上已改为 `override_server_args` 发布配置；同时确认 boundary 与 MLX 两个用例确实失败并修复。
- MLX finished-request 测试依赖运行时 bag (testing): ch-wan 确认真实且将其归入本 commit 同步修复：MLX 测试改为 patch `batch_result_processor.mamba_extra_buffer_lazy_enabled`。
- boundary 测试 fixture 仍用过期 mock (testing): ch-wan 将 fixture 改为 `override_server_args(mamba_radix_cache_strategy="extra_buffer", mamba_track_interval=4)` 并移除多个冗余 patch；最终本 PR 单独跑 225 passed / 38 skipped。
- 谓词迁移后遗留的 server_args 死绑定 (design): 用 AST 扫描确认 9 处死绑定，本 PR 删除 4 处（`prepare_for_extend`、`prepare_for_decode`、`prepare_mamba_track_for_verify`、`Inkling.__init__`），其余 5 处在 #34081 删除；同时清理测试中对应冗余 patch。
- 单独合入的 red-CI blocker (correctness): 修复已折入本 PR，独立合入即 green，不再依赖 #34081 先行。
- 谓词唯一定义与双轨职责划分 (design): 确立 mid-resolution（overrides）与 post-publish（runtime_context）双轨一致性约定，为后续 #34095 / #34096 的 bag 化迁移提供范式。

# 风险与影响

- 风险：
 - **配置发布时序风险**：谓词从 ServerArgs 实例读取改为读配置袋后，若 `memory` / `exec` bag 尚未发布（单测隔离、启动早期、非标准初始化路径），会直接抛 `ValueError: config namespace 'memory' not published`。生产路径依赖 publish 先于首次调度完成，但单测必须同步改为 publish 或 patch 新谓词，三个测试文件的修改正说明该迁移面广。
 - **DeepSeek MoE 双流图决策回归**：`_can_dual_stream_graph` 的 `enable_eplb` 读取点从 `server_args.enable_eplb` 变为 `get_exec().moe.enable_eplb`，若 override 顺序或发布时机不同，可能导致双流图启用决策与旧行为不一致，影响 DeepSeek-V2 系列的性能路径。
 - **热路径开销**：`prepare_for_decode` / `prepare_for_extend` 在每步调度中调用袋谓词，本质是两次字典 / 属性查找 + 字符串比较，与原方法调用等价，性能风险低但属于核心调度路径变更。
 - **跨 PR 依赖**：本 PR 是 stack 的第 1/6 环，部分死绑定与后续测试转换依赖 #34081；当前已自洽，但若单独回退或 cherry-pick 需连同测试修复一起。
- 影响：
 - **代码库结构**：全局 ServerArgs 读取从 45 处降至 24 处，read ratchet 的制度盲区（optional-injection）被清除，后续新增代码更难“偷读”进程配置。
 - **开发者与测试者**：凡是依赖 mamba-extra-buffer 开关的测试不能再 fake `get_server_args()`，必须 publish 配置袋或直接 patch 新谓词；mamba 相关单测的写法随之统一到 `override_server_args`。
 - **功能与用户**：行为等价，无 API 变化、无性能回退，用户无感知。
 - **团队演进**：为同一系列的 #34081 / #34095 / #34096 铺路，确立了“解析管线用 ServerArgs 成员、业务代码用 runtime_context 袋谓词、overrides 提供 mid-resolution 等价物”的三层职责边界。
 - 风险标记：核心路径变更 , 跨 PR 依赖 , 测试夹具需同步迁移 , 行为等价重构

# 关联脉络

- PR #34081 config: business code no longer reads the published ServerArgs: 同一 config 读取收敛 stack 的后续一环；本 PR review 中多次引用，负责移除此处遗留的其余 5 处死绑定并推动 ratchet 基线归零。
- PR #34094 config: pin that resolution is reproducible from the raw input: 同一系列的测试配套，钉死 ServerArgs 解析可重现性契约，与本 PR 的读取收敛互为表里。
- PR #34095 config: the runner and scheduler read resolved config from the bags: 将 runner / scheduler 的配置读取进一步迁移到配置袋，延续本 PR 开启的 bag 化方向。
- PR #34096 config: the KV-cache configurator reads the bags: KV 缓存与 mamba 相关配置读取袋化的直接延续，与本 PR 的 mamba-extra-buffer 谓词迁移同属 kv-cache 配置线。