Repository navigation
[#18156][fix] Carry lora_id and share the cache_salt derivation in the KV-aware router #18160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f6624b8
d8b8c2d
8a18e2c
7a1c9c2
bb5207d
f9f2cef
da00f5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,6 +13,7 @@ | |
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import hashlib | ||
| from collections.abc import Sequence | ||
| from typing import Optional | ||
|
|
||
|
|
@@ -46,11 +47,26 @@ def truncate_sha256_hash_to_int64(block_hash: bytes) -> int: | |
|
|
||
|
|
||
| def get_cache_salt_id(cache_salt: str) -> int: | ||
| """Return the cache salt id used by request handling and cache-aware routing.""" | ||
| from blake3 import blake3 | ||
|
|
||
| h = blake3(cache_salt.encode("utf-8")).digest(length=8) | ||
| return int.from_bytes(h, "little", signed=False) | ||
| """Derive the salt id that keys the KV cache reuse namespace. | ||
|
|
||
| Single source of truth shared by the engine and the KV-cache-aware router: | ||
| ``KVCacheManagerV2._derive_reuse_salt`` uses it to build the | ||
| ``ReuseScope.salt`` that seeds the radix-tree root (and therefore every | ||
| block hash the worker publishes in KV cache events), and the serve-layer | ||
| router uses it to recompute those hashes for prefix matching. The two | ||
| derivations MUST stay byte-identical or salted requests silently score | ||
| zero cache hits on every worker (the block-match walk breaks on the first | ||
| mismatching block). | ||
|
|
||
| Note: the V1 C++ ``BlockKeyHasher`` (cpp/tensorrt_llm/batch_manager/ | ||
| blockKey.cpp) does not use this id; it mixes | ||
| ``std::hash<std::string>(cacheSalt)`` directly into the root block hash, | ||
| which is standard-library-implementation-defined and not reproducible | ||
| here. Router-computed v1 hashes for salted requests therefore only match | ||
| workers running KV cache manager V2. | ||
| """ | ||
| digest = hashlib.sha256(cache_salt.encode("utf-8")).digest() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved to module scope in 991de58. |
||
| return int.from_bytes(digest[:8], "little", signed=False) | ||
|
|
||
|
|
||
| def hash_v1_block_key( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we fix the v2 c++ manager instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no salt derivation in the V2 C++ manager to fix — it consumes an id that Python has already derived, so this line is the layer where the two disagreeing derivations actually live.
Tracing it:
EventManager::hashV1BlockKeytakesstd::optional<std::uint64_t> cacheSaltId(eventManager.h:189, mixed ateventManager.cpp:588-590),ReuseScopeon the C++ side holdsstd::optional<std::uint64_t> saltand its own comment says it mirrors Python's (blockRadixTree.h:50-59), andcastReuseScopeaccepts None /ReuseScope/ an int lora id / an object withlora_idandsalt— never a string (nanobind/batch_manager/kvCacheManagerV2.cpp:679-699). The only salt string underkv_cache_manager_v2/isKVCacheStoredBlockData::cacheSalt(eventManager.h:88), which is event payload for consumers and is not read anywhere else in that file.So
str -> uint64happens only in Python, and on main the two Python derivations disagree:KVCacheManagerV2._derive_reuse_salt:sha256(salt).digest()[:8], little-endiankv_cache_hash.get_cache_salt_id:blake3(salt).digest(8), little-endianThis PR keeps the engine's bytes exactly and moves the router onto them, so no id a running worker has already published changes.
If you meant the V1 C++ hasher, then I agree there is a real defect there, but it is a different and breaking one.
blockKey.cpp:367-370mixesstd::hash<std::string>{}(cacheSalt)straight into the root block hash. That is implementation-defined, so the router cannot reproduce it from Python at all and salted requests can never match a V1 worker — which is the limitation I documented onget_cache_salt_idrather than worked around. Fixing it means changing every published V1 block hash, so it wants its own PR and its own compatibility discussion. Happy to take that one if you want it.One thing worth flagging either way: the docstring on
_derive_reuse_salton main claims the derivation "matches C++ blockKey hashing on cacheSalt". It does not, and never did — that is sha256-truncated on one side andstd::hashon the other. That stale comment is a plausible origin for this exact confusion, and the PR replaces it with what the code actually does.Separately, whenever you have a moment: this PR has had no CI at all, since
/bot runneeds someone on your side. I would rather have it red and fix it than merged unrun.