Prhub

#23300 [bug] Fix cache salt and extra keys for prefix cache isolation

原始 PR 作者 pyc96 合并时间 2026-04-22 04:53 文件变更 3 提交数 2 评论 3 代码增减 +16 / -12

执行摘要

修复 cache_salt 隔离失效的 extra_key 传递 bug

Issue #9163 提出的 cache_salt 安全隔离功能部署后未生效:不同 cache_salt 的请求本应使用不同的前缀缓存 key,但由于 extra_key 字段在请求链路(从请求入口到 Scheduler 构造 Req 对象)中断裂,所有请求实际使用相同的缓存 key,使得不同 tenant 之间的缓存隔离彻底失效。这直接违背了 feature 的设计目标——防止多租户场景下前缀缓存泄露隐私(参考《Leaking Secrets from Prefix Caches》论文提出的威胁模型)。

该 PR 是典型的数据传递断裂 bug,代码改动量小但影响面大——直接破坏安全特性。推荐精读以理解 SGLang 请求处理链中 Req 构造的关键节点,建议未来类似 feature 在 PR 中增加端到端集成测试。

讨论亮点

该 PR 没有产生 Review 评论,但从 PR 描述和提交历史来看,Contributor 基于 Issue #9163 发现并定位了 extra_key 断裂问题。Reviewer hnyls2002 直接批准,说明变更正确性较为明显。

实现拆解

  1. 在 Scheduler 入口补传 extra_key:在 python/sglang/srt/managers/scheduler.pyhandle_generate_request 方法中,在构造 Req 对象时新增 extra_key=recv_req.extra_key 参数。该参数原本已存在于 recv_req 对象中,但由于未显式传递给 Req 构造函数,导致字段静默丢失。这是核心修复。
  2. 在 Session 路径同时补传 extra_key:在 python/sglang/srt/session/session_controller.pycreate_req 方法中,同样新增 extra_key=req.extra_key 字段。虽然 Session 场景使用 cache_salt 的场景较少,但保持入口一致性,避免未来出现类似的隔离失效问题。
  3. 增强测试能力:在 test/manual/openai_server/features/test_cache_report.py 中新增 _get_cached_tokens 静态方法将 prompt_tokens_details 的 None 检查封装为 0 值兜底,减少重复代码。同时调整 test_cache_salt_effectiveness 中的断言逻辑:将原来判定 cached_tokens 完全不变的 assert 改为 cached_tokens_2_second > cached_tokens_2_first,即验证不同 salt 隔离下相同 salt 仍能命中(cached_tokens 增长),使得测试逻辑更贴近实际缓存行为。还新增了 --attention-backend=triton 的启动参数,确保测试环境一致。
文件 模块 状态 重要度
python/sglang/srt/managers/scheduler.py 调度器 modified 6.44
python/sglang/srt/session/session_controller.py 会话管理 modified 4.75
test/manual/openai_server/features/test_cache_report.py 缓存报告 modified 5.74

关键符号

handle_generate_request create_req _get_cached_tokens test_cache_salt_effectiveness

关键源码片段

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

核心修复点:在 handle_generate_request 中补传 extra_key 到 Req 构造函数。

# python/sglang/srt/managers/scheduler.py 中的 handle_generate_request 方法(约第 1878 行)
# 修复前:extra_key 字段未从 recv_req 传递到 Req,导致 cache_salt 隔离失效
# 修复后:显式传递 extra_key,使前缀缓存 key 能区分不同 cache_salt# 构造 Req 对象时补传 extra_key
req = Req(
    rid=recv_req.rid,
    origin_input_text=recv_req.origin_input_text,
    origin_input_ids=recv_req.origin_input_ids,
    origin_input_ids_unpadded=recv_req.origin_input_ids_unpadded,
    sampling_params=sampling_params,
    lora_id=recv_req.lora_id,
    session=recv_req.session,
    custom_logit_processor=recv_req.custom_logit_processor,
    stream=recv_req.stream,
    return_logprob=recv_req.return_logprob,
    top_logprobs_num=recv_req.top_logprobs_num,
    token_ids_logprob=recv_req.token_ids_logprob,
    vocab_size=self.model_config.vocab_size,
    eos_token_ids=self.model_config.eos_token_ids,
    require_reasoning=recv_req.require_reasoning,
    return_hidden_states=recv_req.return_hidden_states,
    return_routed_experts=recv_req.return_routed_experts,
    priority=recv_req.priority,
    routing_key=recv_req.routing_key,
    extra_key=recv_req.extra_key, # 修复:补传该字段
    http_worker_ipc=recv_req.http_worker_ipc,
    dllm_config=self.dllm_config,
    time_stats=recv_req.time_stats,
)
python/sglang/srt/session/session_controller.py entrypoint

同步修复 Session 路径中 Req 构造缺失 extra_key 的问题。

# python/sglang/srt/session/session_controller.py 中的 create_req 方法(约第 235 行)
# 修复前:extra_key 未传递,Session 中 Req 的缓存隔离同样失效
# 修复后:与调度器路径保持一致new_req = Req(
    rid=req.rid,
    origin_input_text=None,
    origin_input_ids=input_ids,
    origin_input_ids_unpadded=input_ids_unpadded,
    sampling_params=req.sampling_params,
    lora_id=req.lora_id,
    session=self,
    custom_logit_processor=req.custom_logit_processor,
    stream=req.stream,
    return_logprob=req.return_logprob,
    top_logprobs_num=req.top_logprobs_num,
    token_ids_logprob=req.token_ids_logprob,
    vocab_size=vocab_size,
    eos_token_ids=eos_token_ids,
    require_reasoning=req.require_reasoning,
    return_hidden_states=req.return_hidden_states,
    return_routed_experts=req.return_routed_experts,
    priority=req.priority,
    routing_key=req.routing_key,
    extra_key=req.extra_key, # 修复:补传该字段
    http_worker_ipc=req.http_worker_ipc,
    time_stats=req.time_stats,
)

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 回归风险低:修复仅涉及参数传递,未改变 Req 构造以外的逻辑。测试文件中断言条件的调整(从严格相等改为大于)反而更符合缓存系统的实际行为,降低了误判风险。
  2. Session 场景可能未被充分覆盖:虽然 session_controller.py 补充了 extra_key,但现有测试用例未对 Session 中使用 cache_salt 的场景进行验证,存在潜在遗留问题。
  3. 测试环境约束:测试新增了 --attention-backend=triton 参数,若未来有其他 attention 后端与该参数冲突,该测试可能失效。
  1. 用户影响:所有使用 cache_salt 的多租户部署用户将受益,缓存隔离得以正确生效;此前已部署但未生效的用户需要升级后方可获得隔离。
  2. 系统影响:无性能影响,extra_key 字段在请求对象中早已存在,仅传递路径补齐。
  3. 团队影响:提醒团队在 feature 开发时需关注长调用链中参数的完整性,建议增加集成测试覆盖跨模块参数传递。
核心路径变更 缺少 Session 场景测试覆盖

关联 Issue

#9163 [Feature] Support Cache Salting for secure prefix caching

完整报告

参与讨论