Repository navigation
Architecture
Chat client (OpenAI-compatible, long message history)
│ /v1/chat/completions
▼
vLLM (TP4 or any parallelism, any served model; production recipe untouched)
│ --kv-transfer-config: LMCacheMPConnector
│ (kv_connector_module_path pins the LMCache-shipped connector)
▼
lmcache server (standalone process; ZMQ request port 5555, HTTP mgmt 8080)
│ L1: host RAM buffer (KVP_L1_SIZE_GB, default 64 GB)
▼
L2 persistent tier: nixl_store_dynamic / POSIX → NVMe (KVP_CACHE_ROOT)
eviction controller: LRU, trigger_watermark 0.9, eviction_ratio 0.1
- vLLM computes KV chunks and STORES them through the connector into L1 (host RAM).
- The store controller asynchronously writes each object to L2 as one file per chunk key (temp file + atomic rename — a crash never leaves a half-written object).
- On lookup, L1 is checked first; a miss falls through to L2 on disk. A disk hit is prefetched into L1 and returned to vLLM. The in-memory index starts empty at boot and is populated lazily from the deterministic file names — this is what makes recovery work after both services restart.
- On shutdown,
persist_enabled=truekeeps the L2 files (the default; setting it to false deletes them and breaks the main requirement).
Only this LMCache L2 adapter family is documented with persist/recover semantics:
files are opened per operation (no pre-allocated registry that dies with the process,
no fd-count limit at 1.5 TB scale), and a lookup miss falls through to the
deterministic file on disk. nixl_store (the pre-allocated variant) does not
document restart recovery. POSIX backend is chosen deliberately — the simplest
reliable NVMe path — before any GDS/hybrid exoticism is considered.
The cache service is a separate process. Restarting vLLM cannot erase persistent KV data; restarting the cache service keeps the data files on disk. A mode where cache data only lives in one process's memory does NOT satisfy persistence and is not used.
- model id + model revision (full 40-hex commit SHA)
- tokenizer name + revision
- effective KV-cache dtype (resolved —
autois not accepted) - KV geometry: block size, num KV heads, head dim
- tensor-parallel size
- connector name + pinned connector/vLLM build identity
- max model len
- chat-template digest (sha256 of the rendered template)
The namespace is kvp-<first 32 hex of sha256(canonical JSON of the fields)>.
This is the logical identity id. On the LMCache storage level it is
converted into model_name (CacheIdentity.lmcache_model_name()), which is
what actually separates files on disk: the nixl_store_dynamic adapter names
files <safe_model_name>_<kv_rank>_<group>_<chunkhash>.bin directly under
KVP_CACHE_ROOT (flat layout — there are NO kvp-<hash> directories; the
prefix of each file name is the sanitized model name carrying this namespace).
Any empty/uncertain field ⇒ cache MISS (recompute), never a wrong hit. Changing any
field moves you to a NEW namespace; old data becomes unreachable but is never deleted
automatically (see Storage & Eviction).
| Layer | Where | Model-specific? |
|---|---|---|
| Identity / namespaces | implementation/kvpersist/identity.py |
input data only |
| Budget & quotas | implementation/kvpersist/budget.py |
no |
| Retention ranking | implementation/kvpersist/retention.py |
no |
| lmcache server + L2 adapter | configs/generic/* |
no |
| Identity pins & geometry | configs/<model>/identity.env |
yes |
| vLLM launcher | scripts/run-vllm-with-persist.sh |
yes |
"Model-agnostic" means the mechanism supports multiple deployments — NOT that their KV data is interchangeable. Different models never share KV blocks.
| Failure | Behavior |
|---|---|
| cache miss / partial hit | recompute only the missing part |
| incompatible entry | unreachable by namespace ⇒ miss |
| corrupt / incomplete file | lookup falls through ⇒ recompute; output never wrong |
| interrupted write | atomic rename ⇒ reader sees old or new file, never partial |
| interrupted eviction | same atomicity; missing files are plain misses |
| disk full / budget hit | stores rejected; serving continues without cache writes |
| cache service down | vLLM falls back to uncached serving; correctness unchanged |
| crash of either process | restart ⇒ lazy index rebuild from disk |
A cache failure can never corrupt output: the worst case is recomputation.
-
lmcache server— background, pid fileKVP_CACHE_ROOT/lmcache-server.pid. - vLLM container — name
mimo-pro-tp4-kvp(kv-persistence launcher) or the recipe's own container when using theKV_PERSIST=1integration. -
kvp-admin(console script /python3 -m kvpersist.cli) — reporting and explicit cleanup; never runs automatically.