Skip to content

[None][fix] Drain in-flight requests before clearing the KV cache reuse state - #17163

Open
lowsfer wants to merge 3 commits into
NVIDIA:mainfrom
lowsfer:kvcm-drain-before-clear
Open

[None][fix] Drain in-flight requests before clearing the KV cache reuse state#17163
lowsfer wants to merge 3 commits into
NVIDIA:mainfrom
lowsfer:kvcm-drain-before-clear

Conversation

@lowsfer

@lowsfer lowsfer commented Aug 1, 2026

Copy link
Copy Markdown
Member

Dev Engineer Review

  • WorkerExtension.reset_prefix_cache() now drains active requests before it clears prefix-cache state.
  • shutdown() and clearReusableBlocks() reject cleanup while KV caches remain open in both backends.
  • Destructor paths report shutdown errors and skip teardown when live KV caches remain.
  • C++ and Python guard behavior and error wording are consistent.
  • No configuration or test-list changes were detected.
  • C++ tests: 177 passed, 12 skipped. Python tests: 169 passed, 20 skipped.

QA Engineer Review

  • Added TestLivingKvCacheGuard coverage for both backends.
  • Tests verify API-specific errors, open-cache counts, storage preservation after rejected cleanup, successful cleanup after cache closure, and closure-based count updates.
  • No tests/integration/test_lists/ coverage references were changed.
  • Verdict: needs follow-up.

Description

WorkerExtension.reset_prefix_cache() was missing @control_action_decorator, unlike both of its
neighbours in the same class: update_weights(), whose docstring calls out that it "uses the
control_action_decorator to ensure all active requests are finished"
, and wait_for_engine_idle().
It is a public Ray worker-extension method, so _collective_rpc("reset_prefix_cache") reached
KvCacheManager::clearReusableBlocks() with requests still in flight.

Clearing detaches the whole radix tree, but a live request keeps owning the tree blocks it matched
via SeqBlock::treeBlock, and goes on using them as the parent for the block its close() commits.
resume() deliberately un-commits a partial trailing block so it is re-committed at close, so this
is the normal path rather than a corner case. The resulting block is grafted onto a detached
subtree: unreachable from any root, so the work is silently discarded. On C++ it is worse than
silent, because addOrGetExistingBlock() derives the block size via prev->tokensPerBlock(), which
reads the now-null grandparent link and segfaults in release builds.

This PR:

  1. Adds the decorator, so the reuse state is only cleared once the engine has drained. This is the
    actual fix.
  2. Guards both entry points — clearReusableBlocks() and shutdown(), which additionally frees the
    storage those pages live in — so misuse reports itself at the offending call rather than
    surfacing later as discarded work or a null dereference.

Both backends name the API in the message, and the check counts sequences that are still open
rather than objects that are still referenced, since KvCache::close() / _KVCache.close() drop
the entry.

shutdown() runs from ~KvCacheManager and __del__, which must not propagate, so both report and
skip teardown instead — leaving the storage alive is safer than freeing it under live pages.

Test Coverage

TestLivingKvCacheGuard in tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py,
three tests that run on both backends:

  • clear_reusable_blocks() and shutdown() each reject an open KV cache, naming the API the caller
    actually invoked.
  • The rejection is a no-op: the check runs before any mutation, so the storage survives and the same
    call succeeds once the cache is closed. This is what makes the error recoverable rather than fatal
    — close the sequences and retry.
  • The count tracks close() rather than object liveness: with three caches open, closing one makes
    the message report two, and the call succeeds with all three references still held.

The two backends raise different types — Python raises LogicError, while TLLM_CHECK_WITH_INFO
surfaces as RuntimeError since only RequestSpecificException is translated — so the test accepts
either rather than pretending they agree.

Writing the test surfaced a divergence this PR had claimed to remove: C++ reported KvCache(s) where
Python reported KV cache(s), so the two backends described the same mistake differently. C++ now
uses the Python wording, and the shared text is asserted so they cannot drift apart unnoticed.

Full suite, both backends:

  • TLLM_KV_CACHE_MANAGER_V2_BACKEND=cpp pytest tests/unittest/kv_cache_manager_v2_tests/ — 177 passed, 12 skipped
  • TLLM_KV_CACHE_MANAGER_V2_BACKEND=python pytest tests/unittest/kv_cache_manager_v2_tests/ — 169 passed, 20 skipped

That is +3 on each backend versus the pre-change counts (174 / 166), matching the three new tests and
confirming they execute under both.

PR Checklist

Please review the following before submitting your PR:

  • PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.

  • PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.

  • Test cases are provided for new code paths (see test instructions)

  • If PR introduces API changes, an appropriate PR label is added - either api-compatible or api-breaking. For api-breaking, include BREAKING in the PR title.

  • Any new dependencies have been scanned for license and vulnerabilities

  • CODEOWNERS updated if ownership changes

  • Documentation updated as needed

  • Update tava architecture diagram if there is a significant design change in PR.

  • The reviewers assigned automatically/manually are appropriate for the PR.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

🤖 Generated with Claude Code

@lowsfer

lowsfer commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

KV cache cleanup now rejects operations while caches remain open. Native and Python destructors report shutdown failures. Prefix-cache reset now drains active requests before invalidating the cache.

Changes

KV cache lifecycle safety

Layer / File(s) Summary
Native cleanup guards
cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h, cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
The native manager checks for living KV caches before shutdown and reusable-block clearing. Its destructor logs shutdown exceptions.
Python cleanup guards
tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py
The Python manager raises LogicError when cleanup starts with open caches. Its destructor emits a warning when shutdown fails.
Prefix reset coordination
tensorrt_llm/llmapi/rlhf_utils.py
reset_prefix_cache now drains in-flight requests before detaching the prefix-cache radix tree.
Lifecycle guard validation
tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py
Tests cover cross-backend exceptions, cleanup diagnostics, storage preservation, closure-based cache counts, and successful cleanup after caches close.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: allisonlim-nv

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes draining in-flight requests before clearing KV cache reuse state.
Description check ✅ Passed The description covers the issue, solution, safeguards, tests, results, and checklist requirements in sufficient detail.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp (1)

147-149: 🩺 Stability & Availability | 🔵 Trivial | 🏗️ Heavy lift

Add regression tests for both lifecycle backends.

Test cleanup with one open cache and with a closed cache. Assert that shutdown() and clearReusableBlocks() reject open caches, preserve storage, and succeed after close. Test destructor paths separately so guard errors are logged or warned without propagating.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp` around
lines 147 - 149, Add regression coverage for both lifecycle backends around
KvCacheManager::shutdown() and clearReusableBlocks(). Verify open caches are
rejected while storage remains intact, closed caches allow both operations to
succeed, and repeat each scenario with one open and one closed cache. Exercise
destructor cleanup separately, asserting guard failures are logged or warned
without propagating exceptions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp`:
- Line 168: Update the argument passed to _checkNoLivingKvCaches in
clearReusableBlocks() to report the native C++ method name,
clearReusableBlocks(), instead of the Python-style clear_reusable_blocks().

In `@tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py`:
- Around line 297-300: Update the warnings.warn call in the cleanup exception
handler around self.shutdown() to pass stacklevel=2, so the warning points to
the cleanup trigger instead of __del__.

---

Nitpick comments:
In `@cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp`:
- Around line 147-149: Add regression coverage for both lifecycle backends
around KvCacheManager::shutdown() and clearReusableBlocks(). Verify open caches
are rejected while storage remains intact, closed caches allow both operations
to succeed, and repeat each scenario with one open and one closed cache.
Exercise destructor cleanup separately, asserting guard failures are logged or
warned without propagating exceptions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2fbb04da-0a9c-46d8-8826-550c56b53ce9

📥 Commits

Reviewing files that changed from the base of the PR and between fdf7bd5 and 1b2c042.

📒 Files selected for processing (4)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h
  • tensorrt_llm/llmapi/rlhf_utils.py
  • tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py

Comment thread tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63227 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63227 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63235 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63235 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63237 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63237 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 1, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63240 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63240 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63252 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63252 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63256 [ run ] triggered by Bot. Commit: 1b2c042 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63256 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer
lowsfer force-pushed the kvcm-drain-before-clear branch from 1b2c042 to 13114f7 Compare August 2, 2026 04:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py`:
- Around line 992-1019: Strengthen
test_clear_reusable_blocks_rejects_open_kv_cache and
test_shutdown_rejects_open_kv_cache by creating committed reusable radix-tree
content and allocated work before invoking the cleanup API with an open cache.
After each expected GuardError, verify the content remains reusable and the
manager can continue allocating or resume work, then close the cache and perform
the successful cleanup.
- Around line 1030-1041: Update the cleanup in the test around
clear_reusable_blocks so caches[0] is closed regardless of whether the first
assertRaises/assertIn sequence succeeds. Ensure the finally block closes every
cache, while preserving the intermediate close and assertions used to verify the
open-cache counts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a22a3f13-aa3a-4c87-93e8-5da904c8cf58

📥 Commits

Reviewing files that changed from the base of the PR and between 1b2c042 and 13114f7.

📒 Files selected for processing (5)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h
  • tensorrt_llm/llmapi/rlhf_utils.py
  • tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py
  • tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
  • tensorrt_llm/llmapi/rlhf_utils.py

@BowenFu BowenFu left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The control_action_decorator on reset_prefix_cache() is clearly right — reset_reuse_state() forwards straight to impl.clear_reusable_blocks() without closing anything, so the drain is genuinely load-bearing. Two things before this lands:

1. The shutdown() guard turns a completed teardown into a leaked pool. shutdown() checks first, then clears and destroys storage — so when the check throws, _storage.destroy() / the C++ teardown never runs, and __del__ / ~KvCacheManager swallow it. The only in-tree caller, KVCacheManagerV2.shutdown(), closes every kv_cache_map entry before calling impl.shutdown(), so the guard should be unreachable there. That leaves the question the PR does not answer: can anything hold an open KvCache outside kv_cache_map (fatal-error path, warmup, transient prefetch caches, a direct binding user)? If no, the guard is dead code on this entry point and a warning would be safer. If yes, the new failure mode is a silent GPU-pool leak in a long-lived process instead of a loud error — worse to diagnose than what it replaces. Consider having shutdown() close the living caches itself (mirroring the wrapper) rather than refuse, and keeping the hard throw only on clear_reusable_blocks() where the caller can actually recover.

2. The description contradicts the diff. "Test Coverage: No new test. ... a regression test would only pass on one backend" — but the diff adds TestLivingKvCacheGuard with three cross-backend tests. Worth fixing; a reviewer who reads "no new test" on a KV-cache lifecycle change will stop there.

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63264 [ run ] triggered by Bot. Commit: 13114f7 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63264 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63271 [ run ] triggered by Bot. Commit: 13114f7 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63271 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

@BowenFu Thanks — point 2 was right, the description was stale and is now updated (the tests landed after it was written).

On point 1, I don't think the premise holds. impl.shutdown() isn't reached only through the wrapper — it's a public API of the library (.def("shutdown", ...), kvCacheManagerV2.cpp:1752), and the KVCM2 unit suites are direct callers: 38 .shutdown() calls under tests/unittest/kv_cache_manager_v2_tests/, none via KVCacheManagerV2 (kv_cache_map appears 3× there, only for reading a cache). They construct KVCacheManager standalone. So the "direct binding user" isn't hypothetical — it's the majority of callers, and it's where the guard earns its keep. That also settles the dead-code branch: prefetch_for_context_tokens() (kv_cache_manager_v2.py:3724) creates a transient cache explicitly "NOT registered in kv_cache_map / IndexMapper".

On the leak, the trigger can't arise. KvCache owns its manager — mManager(manager.shared_from_this()) (kvCache.cpp:63, KvCacheManager : public std::enable_shared_from_this), and Python's _KVCache._manager is a strong ref — so while any live cache exists the manager's refcount cannot reach zero and ~KvCacheManager / __del__ cannot run. If it somehow did, that would be a lifetime bug in its own right, not a case for softening the guard: mLivingKvCaches is std::set<KvCache*>, raw and non-owning, dereferenced at kvCacheManager.cpp:730/:864, so the failure there is a use-after-free rather than a leak.

On closing the living caches inside shutdown() instead of throwing — I think that trades the leak for something strictly worse. close() returns the pages to the pool and destroy() then frees the storage, while the live request's cached base page indices still point into it and its GPU work may still be reading those pages. That's an illegal memory access, not a leak — the same class of failure this PR exists to remove (clearing the tree under live requests already segfaults in release via prev->tokensPerBlock()). A leaked pool is diagnosable; a UAF in a release build is what we're trying to stop shipping. Hence the current choice: report and skip teardown, since leaving storage alive is safer than freeing it under live pages.

On recoverability: the check is the first statement in shutdown(), before clearReusableBlocks() and mStorage->destroy(), so a rejected call mutates nothing — close the sequences and call it again. TestLivingKvCacheGuard asserts exactly that.

lowsfer added 3 commits August 2, 2026 13:47
…se state

WorkerExtension.reset_prefix_cache() was missing @control_action_decorator,
unlike both of its neighbours: update_weights(), whose docstring calls out that
it "uses the control_action_decorator to ensure all active requests are
finished", and wait_for_engine_idle(). It is a public Ray worker-extension
method, so _collective_rpc("reset_prefix_cache") reached
KvCacheManager::clearReusableBlocks() with requests still in flight.

Clearing detaches the whole radix tree, but a live request keeps owning the tree
blocks it matched via SeqBlock::treeBlock, and goes on using them as the parent
for the block its close() commits. resume() deliberately un-commits a partial
trailing block so it is re-committed at close, so this is the normal path, not a
corner case. The resulting block is grafted onto a detached subtree: unreachable
from any root, so the work is silently discarded. On C++ it is worse than
silent, because addOrGetExistingBlock() derives the block size via
prev->tokensPerBlock(), which reads the now-null grandparent link and segfaults
in release builds.

Add the decorator so the reuse state is only cleared once the engine has
drained. Also guard both entry points -- clearReusableBlocks() and shutdown(),
which frees the storage those pages live in -- so misuse reports itself at the
offending call rather than surfacing later as discarded work or a null
dereference. Both backends name the API in the message, and the check counts
sequences that are still open rather than objects that are still referenced,
since KvCache::close() / _KVCache.close() drop the entry.

shutdown() runs from ~KvCacheManager and __del__, which must not propagate, so
both report and skip teardown instead -- leaving the storage alive is safer than
freeing it under live pages.

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
…ross backends

The guards added with the drain fix had no regression coverage: every existing
clear_reusable_blocks() call site closes its caches first, so nothing exercised
the rejection path, and nothing pinned the promise that a rejected call leaves
the storage intact.

Add TestLivingKvCacheGuard: both entry points reject an open cache, the
operation succeeds once it is closed, and the count tracks close() rather than
object liveness -- the last one matters because the guard counts sequences that
are still open, not objects that are still referenced.

Writing it surfaced a divergence the fix had claimed to remove: C++ said
"KvCache(s)" where Python said "KV cache(s)", so the two backends reported the
same mistake differently. Use the Python wording in C++ so the message is
identical either way, and assert the shared text so the two cannot drift apart
again unnoticed.

The exception types still differ -- Python raises LogicError while
TLLM_CHECK_WITH_INFO surfaces as RuntimeError, since only
RequestSpecificException is translated -- so the test accepts either rather than
pretending they agree.

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
The guards were exercised against an empty radix tree: the caches were created
but never resumed or committed, so "the rejected call left the storage intact"
had nothing to observe. An implementation that cleared the tree and *then*
raised passed just as well as the correct one.

Seed committed, reusable content first, and assert it is still reusable after
the rejection -- then assert it is gone after the call that is allowed through,
so the first check cannot pass vacuously. Verified by mutation: reordering
clear_reusable_blocks() to clear before checking now fails with 0 != 64, and
neutering the clear fails the follow-up assertion. For shutdown(), which frees
the storage rather than just the tree, also allocate and resume a fresh sequence
after the rejection to show the pool itself survived.

Close every cache in the counting test's cleanup rather than all but the first.
The first was closed inside the try block, so an assertion failing ahead of it
left it open, and tearDown's shutdown() then raised the guard error over the top
of the real failure. close() is idempotent, so closing all of them is enough.

Signed-off-by: Yao Yao <lowsfer@users.noreply.github.com>
@lowsfer
lowsfer force-pushed the kvcm-drain-before-clear branch from 13114f7 to 3681f7f Compare August 2, 2026 13:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py (1)

315-321: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new lifecycle precondition.

Add Google-style docstrings to shutdown() and clear_reusable_blocks(). State that all KV caches must be closed and that LogicError is raised otherwise.

The PR objective identifies these methods as public direct-binding APIs. As per coding guidelines, prefer docstrings for external interfaces and document public function arguments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py` around
lines 315 - 321, Add Google-style docstrings to the public methods shutdown()
and clear_reusable_blocks(), documenting that all KV caches must be closed
before invocation and that LogicError is raised when living caches remain;
include argument documentation where applicable while preserving the existing
lifecycle checks and behavior.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py`:
- Around line 315-321: Add Google-style docstrings to the public methods
shutdown() and clear_reusable_blocks(), documenting that all KV caches must be
closed before invocation and that LogicError is raised when living caches
remain; include argument documentation where applicable while preserving the
existing lifecycle checks and behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c5c5d48a-2a76-44a3-bb02-27ceb69ffea8

📥 Commits

Reviewing files that changed from the base of the PR and between 13114f7 and 3681f7f.

📒 Files selected for processing (5)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h
  • tensorrt_llm/llmapi/rlhf_utils.py
  • tensorrt_llm/runtime/kv_cache_manager_v2/_core/_kv_cache_manager.py
  • tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.h
  • tensorrt_llm/llmapi/rlhf_utils.py
  • cpp/tensorrt_llm/batch_manager/kv_cache_manager_v2/kvCacheManager.cpp
  • tests/unittest/kv_cache_manager_v2_tests/test_kv_cache_manager_v2.py

@lowsfer
lowsfer requested a review from BowenFu August 2, 2026 14:00
@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63281 [ run ] triggered by Bot. Commit: 3681f7f Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63281 [ run ] completed with state DISABLED
Pipeline is freezed and top-1 instance is under maintenance. For urgent request, contact Yiteng Niu

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63290 [ run ] triggered by Bot. Commit: 3681f7f Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63290 [ run ] completed with state FAILURE. Commit: 3681f7f
/LLM/main/L0_MergeRequest_PR pipeline #51285 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@lowsfer

lowsfer commented Aug 2, 2026

Copy link
Copy Markdown
Member Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63307 [ run ] triggered by Bot. Commit: 3681f7f Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #63307 [ run ] completed with state SUCCESS. Commit: 3681f7f
/LLM/main/L0_MergeRequest_PR pipeline #51302 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@BowenFu

BowenFu commented Aug 3, 2026

Copy link
Copy Markdown

Read 3681f7f23. The guard is the right shape and the tests are good — _seed_reusable_prompt in particular makes test_clear_reusable_blocks_rejects_open_kv_cache non-vacuous, and test_guard_counts_only_open_caches pins that the count tracks close() rather than object liveness.

One thing I'd like your read on before approving: the rejected clear_reusable_blocks() is a no-op, but the rejected shutdown() is not.

_checkNoLivingKvCaches("shutdown()") runs first, so when it throws, neither clearReusableBlocks() nor the storage teardown below it runs. Both destructors now swallow that throw — C++ catches std::exception and logs, Python catches LogicError and warns. So a manager that is destroyed with a KV cache still open used to free its storage unconditionally and now leaks it, with one ERROR line as the only signal. That is a change to an existing teardown path, not just to the explicit API.

Two shapes that avoid it: reject only the explicit API call and let the destructor force the teardown, or have shutdown() close/drain whatever is still open instead of refusing. Either way the destructor path deserves a test — test_shutdown_rejects_open_kv_cache covers the explicit call and asserts the pool survives, but nothing covers the __del__/dtor path that the new try/catch exists for.

Minor: shutdown() calls clearReusableBlocks(), so on the success path the check runs twice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants