执行摘要
- 一句话:修复HiMamba墓碑级联删除忽略host引用保护
- 推荐动作:建议阅读此 PR 以理解多级缓存中 host 引用计数在逐出算法中的关键作用。测试设计值得借鉴:通过模拟
HiMambaRadixCache 内部状态(而不依赖完整初始化)来高效验证边界条件。但需注意当前修复仍是临时方案,后续应跟踪 UnifiedRadixTree 迁移进展。
功能与动机
此PR是#22592的后续修复。用户@riZZZhik在#22592中报告在持续高负载下仍会触发相同的AssertionError: parent does not have child key崩溃。经分析,_iteratively_delete_tombstone_leaf在级联删除时只检查了full_lock_ref和mamba_lock_ref,但忽略了host_ref_counter和host_mamba_ref_counter,导致host节点(如正在prefetch/backup)被错误删除,破坏父->子关系。详见PR body和评论。
实现拆解
- 在
hi_mamba_radix_cache.py 的 _iteratively_delete_tombstone_leaf 方法中,在 while 循环内新增一组条件判断:如果当前父节点的 host_ref_counter > 0 或 host_mamba_ref_counter > 0,则立即停止级联删除。这确保了正在被 host 引用(如正在 prefetch/backup)的节点不会被错误地从树中移除。
- 在
mamba_radix_cache.py 的 cache_unfinished_req 方法中,调用 self.insert() 时传入 chunked=chunked 参数(之前遗漏),以对齐基类 RadixCache.cache_unfinished_req 的行为,避免因缺少该参数导致的分片处理错误。
- 在
test/registered/unit/mem_cache/test_mamba_unittest.py 中新增测试方法 test_hi_mamba_tombstone_cleanup_respects_host_ref,通过构造一个 HiMambaRadixCache 实例并手动设置 host_ref_counter,验证级联删除不会移除受 host 引用的父节点。
- 在
test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py 中新增测试方法 test_tombstone_cleanup_respects_locked_parent,验证 UnifiedRadixCache._iteratively_delete_tombstone_leaf 在父节点 lock_ref > 0 时也不会错误删除,确保类似保护存在于统一缓存层。
关键文件:
python/sglang/srt/mem_cache/hi_mamba_radix_cache.py(模块 HiMamba;类别 source;类型 core-logic;符号 _iteratively_delete_tombstone_leaf): 核心修复文件:在 _iteratively_delete_tombstone_leaf 中新增 host_ref_counter/host_mamba_ref_counter 检查,防止受 host 引用的节点被级联删除。
test/registered/unit/mem_cache/test_mamba_unittest.py(模块 单元测试;类别 test;类型 test-coverage;符号 test_hi_mamba_tombstone_cleanup_respects_host_ref, RecordingCacheController, init, evict_device): 新增单元测试 test_hi_mamba_tombstone_cleanup_respects_host_ref,直接验证 hi_mamba_radix_cache 中 host 引用保护逻辑的正确性。
python/sglang/srt/mem_cache/mamba_radix_cache.py(模块 Mamba;类别 source;类型 core-logic;符号 cache_unfinished_req): 在 cache_unfinished_req 中补传 chunked 参数,对齐基类行为,避免分片处理错误。
test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py(模块 单元测试;类别 test;类型 test-coverage;符号 test_tombstone_cleanup_respects_locked_parent): 新增测试 test_tombstone_cleanup_respects_locked_parent,验证 UnifiedRadixCache 中类似的 lock_ref 保护,确保统一缓存层也具备防御措施。
关键符号:_iteratively_delete_tombstone_leaf, cache_unfinished_req, test_hi_mamba_tombstone_cleanup_respects_host_ref, test_tombstone_cleanup_respects_locked_parent
关键源码片段
test/registered/unit/mem_cache/test_mamba_unittest.py
新增单元测试 test_hi_mamba_tombstone_cleanup_respects_host_ref,直接验证 hi_mamba_radix_cache 中 host 引用保护逻辑的正确性。
def test_hi_mamba_tombstone_cleanup_respects_host_ref(self):
# 手工构造 HiMambaRadixCache 内部状态,避免复杂初始化
tree = object.__new__(HiMambaRadixCache)
root = TreeNode()
parent = TreeNode()
deleted = TreeNode()
root.key = RadixKey([])
parent.key = RadixKey([1])
deleted.key = RadixKey([2])
parent.parent = root
deleted.parent = parent
parent.value = torch.tensor([1], dtype=torch.int64)
parent.protect_host() # 设置 host_ref_counter > 0
root.children[parent.key.child_key(1)] = parent
# 使用 RecordingCacheController 记录驱逐事件
class RecordingCacheController:
def __init__(self):
self.device_evictions = []
self.host_evictions = []
def evict_device(self, value):
self.device_evictions.append(value)
def evict_host(self, value):
self.host_evictions.append(value)
tree.root_node = root
tree.page_size = 1
tree.full_lru_list = LRUList(mamba=False)
tree.full_lru_list.insert_mru(parent)
tree.cache_controller = RecordingCacheController()
tree.full_evictable_size_ = len(parent.value)
tree.evictable_full_device_leaves = {parent}
tree.evictable_full_host_leaves = set()
result_node, full_evicted, mamba_evicted = (
tree._iteratively_delete_tombstone_leaf(deleted)
)
# 断言:节点没有被级联删除,父节点仍在树中
self.assertIs(result_node, deleted)
self.assertEqual(full_evicted, 0)
self.assertEqual(mamba_evicted, 0)
self.assertIs(root.children[parent.key.child_key(1)], parent)
self.assertTrue(tree.full_lru_list.in_list(parent))
self.assertEqual(tree.cache_controller.device_evictions, [])
self.assertEqual(tree.cache_controller.host_evictions, [])
评论区精华
@riZZZhik 报告在应用 patch 后,中等负载下仍出现相同错误,但随后确认错误由其他因素引起(相关 Issue 评论)。
@icepoint666 分析推测另一个潜在 BUG:_insert_helper_host() 更新 last_access_time 并刷新 mamba LRU,但未刷新 full LRU 中仍在 device 上的节点,可能导致后续 LRU 一致性问题。
@riZZZhik 最后确认本PR在高负载下测试通过。
- AssertionError 在中等负载下仍然出现 (correctness): @riZZZhik 后来确认错误由其他因素引起,本 PR 修复有效。但 icepoint666 提出的 full LRU 刷新问题未在本 PR 中解决。
- 确认修复在高负载下有效 (testing): 修复通过用户实际高负载验证,无回归。
风险与影响
- 风险:主要风险是本修复仅解决了 host 引用保护,但未处理
_insert_helper_host 不刷新 full LRU 的潜在问题(icepoint666 指出)。该问题可能导致 LRU 顺序错误进而影响逐出策略。不过当前修复是官方声明的临时方案,长期计划是迁移到 UnifiedRadixTree。此外,新增的测试覆盖了 host 引用保护的关键路径,但未覆盖高并发场景下的完整行为。
- 影响:影响范围限于使用 HiMambaRadixCache + Mooncake 存储 + write_through 策略的用户。修复后预计消除了持续高负载下的断言崩溃。对 MambaRadixCache 的修改(传递 chunked 参数)是对齐基类行为,不影响现有功能。测试的增强为未来重构提供了安全网。
- 风险标记:host引用保护遗漏
关联脉络
- PR #22592 (原PR标题未知,但本PR是它的后续修复): PR #22592 引入了 HiMambaRadixCache 但遗漏了 host_ref 检查,导致本PR修复的bug。
参与讨论