Skip to content

[HiCache] Preserve restored SWA lock ownership - #36350

Closed
paulzhang-tm wants to merge 5 commits into
sgl-project:mainfrom
paulzhang-tm:codex/fix-restored-swa-lock-ownership
Closed

paulzhang-tm wants to merge 5 commits into
sgl-project:mainfrom
paulzhang-tm:codex/fix-restored-swa-lock-ownership

Conversation

@paulzhang-tm

@paulzhang-tm paulzhang-tm commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Motivation

HiCache stores Full KV, SWA KV, and Mamba state as separate cache components that may be evicted and restored independently. A request whose lock skipped a tombstoned SWA node could later early-release an SWA lock that another request acquired after the node was restored: release_window_lock walked the window without consulting the acquiring request's skip set, so it decremented a lock it never owned.

Scope after review

An earlier revision also relaxed the cascade-eviction assertion (assert cd.lock_ref == 0 on a strictly lower-priority component) so that SWA eviction would spare a locked Mamba value. Review asked for a concrete trigger. The state needs HiCache with Mamba present and no SWA host pool: only then does the SWA match validator accept tombstones, only then does a load-back restore Full and Mamba without SWA, and only then can the request lock take Mamba while skipping SWA. No server-args configuration yields that layout. {FULL, SWA, MAMBA} matches only _MambaSwaStrategy, which always builds an SWA host pool, so load-back restores SWA before the request lock is taken. The only layout without an SWA host pool is DeepSeek V4 unified_kv ({FULL, SWA}, plus a C128 sidecar on NPU with priority 2 and no locks), which has no Mamba. Node splits zero the new parent's Mamba lock, and swa_retain_floor keeps the Mamba checkpoint node inside the retained SWA window, so no other path produces the state either. The assertion is therefore a correct defensive invariant, and this revision restores it unchanged in both the Python and Rust tree cores.

Modifications

  • Replay the acquiring request's SWA skip set during early window-lock release (dec_swa_lock_only -> release_window_lock), in the Python and Rust tree cores.
  • Extend the TestDecSwaLockSkip fake in test_mamba_donated_alloc_ratio.py and the Rust components/swa.rs tests to cover the skip-set plumbing.

Accuracy Tests

Not applicable; this changes lock bookkeeping without changing model computation.

Focused unit tests (test_unified_radix_cache_unittest.py -k "FULL_SWA_MAMBA or TestUnifiedTreeCore" plus test_mamba_donated_alloc_ratio.py):

81 passed, 224 skipped, 4 subtests passed
8 passed

Speed Tests and Profiling

Not applicable; the change adds a constant-time skip check to an existing release path.

Checklist

  • Format code with the repository's pinned Black and isort versions.
  • Pass the repository's Ruff rule set.
  • Add unit tests for the lock-ownership race.
  • No documentation changes are required.

-Robot

🤖 Generated with Claude Code


CI States

Latest PR Test (Base): ❌ Run #33767805627
Latest PR Test (Extra): ❌ Run #33767805402
Latest PR Test (AMD ROCm 7.2): ❌ Run #33767805697

@ispobock

Copy link
Copy Markdown
Collaborator

/rerun-group radix_cache/unified_radix_tree hicache

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Results for /rerun-group radix_cache/unified_radix_tree hicache:

🚀 4-gpu-h100 (6 tests): ✅ View workflow run

cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_hicache_pp_kl.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_cp.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_dsv4.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_mamba.py
cd test/ && python3 registered/hicache/test_pp_with_hicache.py
cd test/ && python3 registered/hicache/test_qwen35_hicache.py

🚀 4-gpu-b200 (1 test): ❌ View workflow run

cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_dcp.py

🚀 8-gpu-h200 (3 tests): ✅ View workflow run

cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_dsv4_pp.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_mimo.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_nightly.py

🚀 2-gpu-h100 (7 tests): ✅ View workflow run

cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_full.py
cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_swa.py
cd test/ && python3 registered/hicache/test_hicache_spec_mooncake_storage.py
cd test/ && python3 registered/hicache/test_hicache_storage_3fs_backend.py
cd test/ && python3 registered/hicache/test_hicache_storage_file_backend.py
cd test/ && python3 registered/hicache/test_hicache_storage_mooncake_backend.py
cd test/ && python3 registered/hicache/test_hicache_storage_runtime_attach_detach.py

🚀 1-gpu-h100 (3 tests): ✅ View workflow run

cd test/ && python3 registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_hybrid_bitexact.py
cd test/ && python3 registered/hicache/test_hicache_spec_file_storage.py
cd test/ && python3 registered/hicache/test_hicache_variants.py

🚀 1-gpu-5090 (1 test): ✅ View workflow run

cd test/ && python3 registered/hicache/test_hicache_storage.py

registered/hicache/test_hicache_storage_umbp_backend.py: No register_cuda_ci(runner_config=...) or register_cpu_ci() found in test/registered/hicache/test_hicache_storage_umbp_backend.py. This file may not be a registered CI test.

@paulzhang-tm
paulzhang-tm force-pushed the codex/fix-restored-swa-lock-ownership branch 2 times, most recently from b57b819 to 3cd2f33 Compare September 1, 2026 14:56
Replay the acquiring request’s SWA skip set during early release so a restored tombstone’s lock remains owned by the request that acquired it.

Treat device and host locks as unconditional ownership barriers during cascade eviction, including on lower-priority Mamba state, and cover both restoration races with regression tests.
@paulzhang-tm
paulzhang-tm force-pushed the codex/fix-restored-swa-lock-ownership branch from 3cd2f33 to 0cf22e2 Compare September 1, 2026 14:59

@ispobock ispobock left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release_window_lock skip-set replay fix looks good for me.

For the cascade change, I remember the assert is necessary to keep cc @hzh0425
The cascade takes Mamba out with SWA unless it's locked, and a request locks SWA and Mamba together at best_match_node, so how does a node end up Mamba-locked with its SWA device-evicted? The new test hand-builds that state in _build_core() instead of reaching it through match to evict. @paulzhang-tm Could you provide a concrete sequence to trigger the assert?

@ispobock

ispobock commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

ref: #36848 is refactoring the skip_lock_node_ids

paulzhang-tm and others added 3 commits September 2, 2026 16:00
Replace the hand-built TestUnifiedTreeCoreRestoredSWALocks fixture with
three UnifiedRadixCacheSuite tests that reach the same state through the
production path in the unified_kv + HiCache layout, where SWA has no host
pool: a Full-evicted leaf is loaded back as Full+Mamba only, match_prefix
selects it, the request lock takes Mamba and skips SWA, an overlapping
insert restores SWA under the held Full lock, and SWA eviction cascades
into the still-locked Mamba.

On the merge-base the cascade test trips `assert cd.lock_ref == 0` in
_should_cascade_evict_component and the early-release test observes the
second request's SWA lock being released; both pass with the fix.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fold the three test_ring_mode_* tests and their helper into a single
linear test, test_swa_evict_cascade_spares_mamba_locked_while_swa_was_missing,
that walks the unified_kv + HiCache sequence step by step: write-through,
Full demote, load-back of Full+Mamba without SWA, match and request lock
that skips the missing SWA, overlapping insert restoring SWA under the held
Full lock, and the SWA eviction whose cascade meets the Mamba lock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Reach the Mamba-locked, SWA-missing node with the real HiCache controller
instead of tree-core calls: init_hicache, write-through on insert, evict,
match_prefix, load_back + loading_check, inc_lock_ref.  The only setup step
that is not a cache call declares the layout under test (an SWA component
with no host pool, as the unified_kv assembler leaves it), since no stack
strategy builds HiCache with Mamba and without an SWA host pool today.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@paulzhang-tm

Copy link
Copy Markdown
Contributor Author

@ispobock check test_swa_evict_cascade_spares_mamba_locked_while_swa_was_missing

@ispobock

ispobock commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

The test drives production APIs but on a pool config the assembler never builds: {FULL,SWA,MAMBA} only matches _MambaSwaStrategy, which always allocates a SWA host pool, so has_swa_host_pool is True for any real Full+SWA+Mamba model and SWA is restored on load-back. The only SWA-no-host-pool layout is DSV4/unified_kv {FULL,SWA}, which has no Mamba. So what server-args config actually yields has_swa_host_pool=False with Mamba present? If none, this is a correct defensive invariant rather than a reachable bug.

Review asked for a concrete trigger for the relaxed cascade check.  The
Mamba-locked, SWA-missing node needs HiCache with Mamba present and no SWA
host pool, and no server-args configuration builds that layout:
{FULL, SWA, MAMBA} only matches _MambaSwaStrategy, which always allocates an
SWA host pool, so load-back restores SWA before the request lock is taken;
the only SWA-no-host-pool layout is DeepSeek V4 unified_kv, which has no
Mamba.  The assertion is therefore a correct defensive invariant.

Restore _should_cascade_evict_component and its Rust counterpart to their
original form, put the original cascade tests back, and drop the cascade
regression test.  The skip-set replay in release_window_lock and its tests
stay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@ispobock

ispobock commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

#36848 is already merged, skip_lock_node_ids is replaced by segment lock now.

@ispobock

Copy link
Copy Markdown
Collaborator

The issue seems to be gone on the main branch. I may close this PR for now. Feel free to reopen it if the issue persists.

@ispobock ispobock closed this Sep 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants