Prevent stale CP HiCache tails from overlapping new page owners

CP HiCache owns KV at page granularity, but exact valid-tail extension and backed partial-tail split could leave an old sub-page tail child beside a new suffix that reuses the same physical page. That makes radix residency ambiguous across device, host, and draft mirrors. The insert/match split paths now prune stale floored tails when safe, and defer/fail through the existing pending-split path when the subtree is protected or has in-flight backup state.\n\nThis also keeps a temporary scheduler boundary warning for externally observed zero-output responses so future ETE runs can classify whether zero visible output reaches SGLang's output processor.\n\nConstraint: CP shared KV and HiCache manage physical KV by page, while radix keys retain valid-token lengths.\nRejected: Keep overlapping old tail nodes after page-floor split | leaves two independent cache states for one physical tail page.\nRejected: Force-prune protected or in-flight backup tails | can mutate cache state still used by active transfer or inference.\nConfidence: medium\nScope-risk: moderate\nDirective: Do not remove the stale-tail prune without replacing it with another page-granular ownership rule for CP HiCache radix splits.\nTested: Remote py_compile for hiradix_cache.py and scheduler_output_processor_mixin.py in g0034 container.\nTested: Remote PYTHONPATH=python python -m pytest -q test/registered/unit/mem_cache/test_cp_hicache_metadata.py -> 97 passed, 5 warnings.\nNot-tested: Full ETE recovery of EAGLE accept length; latest ETE still shows accept collapse, documented in C55.\nNot-tested: Router/client-side output_len=0 correlation when server-side OUTPUT_ZERO_DEBUG does not fire.
This commit is contained in:
laoyao0822
2026-05-30 05:51:53 +08:00
parent e9c341afe8
commit 10296a5fef
4 changed files with 326 additions and 13 deletions
@@ -2646,11 +2646,7 @@ class TestHiRadixCacheCPLoadBack(CustomTestCase):
self.assertEqual(result.last_host_node.host_len, 4)
self.assertEqual(result.last_host_node.cp_hicache.logical_len, 4)
self.assertEqual(result.last_host_node.cp_hicache.padded_len, 4)
child = result.last_host_node.children[(4, 5)]
self.assertEqual(child.key.token_ids, [4, 5])
self.assertEqual(child.host_len, 2)
self.assertEqual(child.cp_hicache.logical_len, 2)
self.assertEqual(child.cp_hicache.padded_len, 4)
self.assertNotIn((4, 5), result.last_host_node.children)
def test_cp_backed_tail_split_before_first_page_returns_root_match(self):
cache = HiRadixCache.__new__(HiRadixCache)
@@ -2790,6 +2786,8 @@ class TestHiRadixCacheCPLoadBack(CustomTestCase):
cache.maybe_bigram_convert = lambda key: (key, None)
cache._update_leaf_status = lambda node: None
cache._update_host_leaf_status = lambda node: None
cache.enable_storage = False
cache.enable_kv_cache_events = False
root = TreeNode()
root.key = RadixKey([])
root.value = torch.empty((0,), dtype=torch.int64)
@@ -2811,7 +2809,7 @@ class TestHiRadixCacheCPLoadBack(CustomTestCase):
self.assertEqual(result.device_indices.tolist(), [0, 1, 2, 3])
self.assertEqual(result.host_hit_length, 0)
self.assertEqual(result.last_device_node.key.token_ids, [0, 1, 2, 3])
self.assertEqual(result.last_device_node.children[(4, 5)].key.token_ids, [4, 5])
self.assertNotIn((4, 5), result.last_device_node.children)
def test_cp_insert_floors_backed_tail_split_to_page_boundary(self):
cache = HiRadixCache.__new__(HiRadixCache)
@@ -2869,10 +2867,7 @@ class TestHiRadixCacheCPLoadBack(CustomTestCase):
self.assertEqual(parent.key.token_ids, [0, 1, 2, 3])
self.assertEqual(parent.cp_hicache.logical_len, 4)
self.assertEqual(parent.cp_hicache.padded_len, 4)
old_tail = parent.children[(4, 5)]
self.assertEqual(old_tail.key.token_ids, [4, 5])
self.assertEqual(old_tail.cp_hicache.logical_len, 2)
self.assertEqual(old_tail.cp_hicache.padded_len, 4)
self.assertNotIn((4, 5), parent.children)
new_tail = parent.children[(4,)]
self.assertEqual(new_tail.key.token_ids, [4])
self.assertEqual(new_tail.value.tolist(), [4])
@@ -2937,7 +2932,7 @@ class TestHiRadixCacheCPLoadBack(CustomTestCase):
self.assertEqual(result.prefix_len, 4)
parent = root.children[(0, 1, 2, 3)]
self.assertEqual(parent.key.token_ids, [0, 1, 2, 3])
self.assertEqual(parent.children[(4, 5)].key.token_ids, [4, 5])
self.assertNotIn((4, 5), parent.children)
new_tail = parent.children[(4, 5, 6, 7)]
self.assertEqual(new_tail.key.token_ids, [4, 5, 6, 7, 8, 9])
self.assertTrue(prepared.attached)