长上下文请求中断后,别人的KV缓存可能写进你的输出:SGLang改为延迟释放槽位
核心概要
SGLang在分离式预填充/解码服务中修复了一类跨请求KV缓存泄漏:请求在传输中途被中止后,已发出的RDMA写仍会落地,可能覆盖另一个请求的缓存。修复让解码端在收到各预填充rank的排空确认或超时前不释放槽位,并在v0.5.21起对Mooncake与NIXL默认开启;2×H100上吞吐差异在±2%以内。
深度剖析
分离式预填充/解码服务里,一个请求被中止后,它已经发出的KV缓存写入仍可能落进另一个请求的槽位,让后者的生成读到别人的上下文。 此前的实现会在请求终止时立即把解码端的目标槽位交还分配器,不等已发出的写完成;这类损坏不报错、不写日志,在常规负载下罕见,长上下文会显著提高概率。 Cloudflare报告其Workers AI上@cf/zai-org/glm-5.3在2026年8月27日至9月9日间受影响,调查未发现其他模型受影响;其配置上下文长度为1,048,576个token,而预填充KV缓存池只能容纳681,280个token。
解码端不再在请求中止时立刻释放槽位,而是等到每个被通知的预填充rank确认已发出的写排空,或等到有界超时,才把槽位交还。 这套机制叫延迟KV缓存释放:中止时解码端向各预填充rank发送ABORT,rank在出队计数归零后回ABORT_ACK,解码端收齐确认才释放,超时则作为兜底。 在2×H200、Mooncake走RDMA的1P1D配置上,一次单边中止释放了供体的430个槽位,受害请求在[1..105]内预分配,供体已发出的写落在解码中途;开启保持后,受害请求输出与基线逐字节一致。
准入路径也一并收紧:被拒绝的请求在进入分离式交接之前就返回错误,不再走完引导握手和一次单token前向。 此前被拒绝的请求仍把自己报告为存活,会完成引导握手、预留元数据缓冲并跑一次前向,之后才被回收;若两侧都拒绝,解码端要等300秒超时才返回。 #38935于2026年9月16日合并,使请求在进入PrefillBootstrapQueue.add或DecodePreallocQueue.add时即被回收;两侧都拒绝时解码端立即返回400。
启示与展望
这套保护针对启用分离式预填充/解码并使用KV缓存传输后端(Mooncake、NIXL、MORI或自定义CommonKVManager后端)的部署;不带跨引擎传输路径的单实例部署不受这一竞态影响。默认配置从v0.4.x系列起受影响,直到v0.5.21,MORI为v0.5.22。运维方被建议升级到v0.5.21或更新版本,并运行会成对取消请求的路由。
超时释放只有在未完成的写届时确已停止时才安全,超时到期本身并不确认写已排空;在预填充与解码使用不同确认格式时,排空握手可能失败并回落到配置的超时。Mooncake发送失败后,在Mooncake能报告失败批次是否排空之前,每次失败都要付出完整保持时间的代价。
