Prhub

#29430 Fix abusing presence of req.req_pool_idx to indicate the presence of req.kv resources

原始 PR 作者 fzyzcjy 合并时间 2026-07-15 14:48 文件变更 6 提交数 17 评论 3 代码增减 +14 / -8

执行摘要

修复以 req_pool_idx 代理判断 KV 资源存在性的问题

来自 PR body:'Stop abusing req.req_pool_idx is not None to mean "owns KV". Where a check really asks about KV ownership, use req.kv is not None. req_pool_idx (a ReqToTokenPool handle) and owned KV are independent resources and should not share one presence flag.' 该变更是 req_pool_idx / cache / owned-KV 解耦重构链的一部分,旨在明确区分两个独立资源的生命周期。

该 PR 是 req_pool_idx / KV 资源解耦的关键环节,设计思路明确,变更谨慎。但需关注 scheduler.py 中可能的断言失败风险,建议在后续版本中添加安全守卫或确认调用点前置条件。值得作为属性解耦的参考案例学习。

讨论亮点

Code review 由 gemini-code-assist[bot] 提出:在 scheduler.pyprefill.py 中无条件调用 release_kv_cache 可能引发断言失败,建议恢复守卫条件。对于 scheduler.py,建议使用 if req.kv is not None or self.tree_cache.supports_mamba();对于 prefill.py,类似建议。最终 prefill.py 被修改为更全面的条件,但 scheduler.py 仍为无条件调用,存在潜在风险。

实现拆解

  1. mem_cache/common.pyrelease_kv_cache 函数中添加断言 assert (req.req_pool_idx is None) == (req.kv is None),强制两个字段生命周期一致,并移除重复的条件检查。
  2. scheduler.pyprocess_pending_chunked_abort 中,移除原来的条件判断 if req.req_pool_idx is not None or self.tree_cache.supports_mamba(),直接调用 release_kv_cache,依赖内部断言。
  3. disaggregation/prefill.pyhandle_bootstrap_failure 中,将条件改为同时检查 req.req_pool_idx is not Nonereq.kv is not Nonereq.mamba_pool_idx is not None,确保在任一资源存在时释放。
  4. invariant_checker.py_get_total_uncached_sizesstreaming_session.pyis_holding_kv 中,将 req.req_pool_idx is None 替换为 req.kv is None,以正确反映 KV 资源的存在。
  5. 在测试文件 req_handle.pykv_pages 属性中,同样使用 req.kv is None 判断。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler.py 调度器 modified 5.6
python/sglang/srt/disaggregation/prefill.py 预填充 modified 5.59
python/sglang/srt/mem_cache/common.py 内存缓存 modified 5.53
python/sglang/srt/managers/scheduler_components/invariant_checker.py 不变量检查器 modified 5.1
python/sglang/srt/session/streaming_session.py 会话管理 modified 5.07
python/sglang/test/scripted_runtime/req_handle.py 请求句柄 modified 3.42

关键符号

process_pending_chunked_abort handle_bootstrap_failure release_kv_cache _get_total_uncached_sizes is_holding_kv find_active_slot kv_pages

关键源码片段

python/sglang/srt/disaggregation/prefill.py core-logic

预填充引导失败处理器,条件判断从单一检查 req_pool_idx 扩展为同时检查 req_pool_idx、kv 和 mamba_pool_idx,更加全面。

def handle_bootstrap_failure(self: Scheduler, req: Req) -> None:
    ...
    req.time_stats.trace_ctx.abort(abort_info={'reason': error_message})
    # 修改点:原条件仅检查 req.req_pool_idx is not None or tree_cache.supports_mamba()
    # 现在同时检查 req.kv 和 req.mamba_pool_idx,覆盖更全面的资源释放场景
    if (
        req.req_pool_idx is not None
        or req.kv is not None
        or req.mamba_pool_idx is not None
    ):
        release_kv_cache(req, self.tree_cache)
    ...
python/sglang/srt/mem_cache/common.py core-logic

内存缓存核心函数 release_kv_cache,添加断言强制 req_pool_idx 和 kv 生命周期一致,是本次重构的核心变更。

def release_kv_cache(req: Req, tree_cache: BasePrefixCache, is_insert: bool = True):
    # 添加断言:确保 req.req_pool_idx 和 req.kv 两个独立资源的生命周期始终保持一致
    # 之前仅通过 req.req_pool_idx 代理判断时容易出现逻辑缠绕
    assert (req.req_pool_idx is None) == (req.kv is None)
​
    # MambaRadixCache 可能在 KV 分配之前分配 mamba 状态
    if req.req_pool_idx is None:
        assert (
            tree_cache.supports_mamba()
        ), '只有 MambaRadixCache 允许在分配前释放'
        if req.mamba_pool_idx is not None:
            tree_cache.req_to_token_pool.mamba_allocator.free(
                req.mamba_pool_idx.unsqueeze(-1)
            )
            req.mamba_pool_idx = None
        return
​
    effective_kv_committed_len = req.effective_kv_committed_len()
    tree_cache.cache_finished_req(
        req,
        is_insert=is_insert and not getattr(req, 'skip_radix_cache_insert', False),
        kv_len_to_handle=effective_kv_committed_len,
    )
​
    # StreamingSession 内部处理 speculative 尾部修剪后再设置 req_pool_idx = None
    assert (req.req_pool_idx is None) == (req.kv is None)
    if req.req_pool_idx is None and req.kv is None:
        return
​
    start_p, end_p = effective_kv_committed_len, req.kv.kv_allocated_len
    ...

评论区精华

scheduler.py 无条件调用 release_kv_cache 的风险 正确性

Code review 指出:移除守卫条件后,在非 Mamba 模型且请求无 KV 资源时,调用 release_kv_cache 会触发断言失败 (assert tree_cache.supports_mamba()),建议恢复守卫 condition。

结论:作者未采纳,最终代码仍为无条件调用。 · not resolved

prefill.py 条件判断的改进 正确性

Code review 建议使用 req.kv 代替 req.req_pool_idx 进行守卫;作者最终改为同时检查 req.req_pool_idx、req.kv 和 req.mamba_pool_idx,更加全面。

结论:条件覆盖更全,问题已解决。 · 已解决

风险与影响

  1. scheduler.py 中移除守卫后,在非 Mamba 模型且请求无 KV 资源时,调用 release_kv_cache 会进入 if req.req_pool_idx is None: 分支,该分支要求 tree_cache.supports_mamba() 为真,否则断言失败。
  2. 新增的内部断言 (req.req_pool_idx is None) == (req.kv is None) 可能暴露已有代码路径中生命周期不一致的 bug,但能提前发现隐患。
  3. 如果未来某个代码路径忘记了设置 req.kv,使用 req.kv is None 判断可能会误判资源存在,但当前断言会第一时间发现不一致。

影响范围包括调度器核心路径、预填充引导失败处理、内存缓存释放、不变量检查、会话管理等模块。改动量小(+14/-8 行),但对逻辑正确性有提升。对用户透明,但可能修复潜在的资源泄漏或错误释放问题。开发者在进行相关功能(如分离部署、投机解码)开发时需注意此解耦。

核心路径变更 断言风险 分支覆盖不足 review 未完全采纳建议

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论