Documentation menu
Integration options

LMCache L2 plugin

The ADP card as the L2 tier of the LMCache multiprocess server, an alternative integration for LMCache deployments. How it fits under LMCache, how to run it, its settings and what to expect.

What it is

The plugin makes the ADP card the second tier (L2) of the LMCache multiprocess server. LMCache keeps all its parts: its interface to vLLM, its index, its policies and its host-memory tier (L1, pinned host memory). Objects that leave L1, and objects that LMCache looks up after a restart, are on the card behind the storage gateway. LMCache loads the plugin through its own native_plugin adapter type. LMCache itself does not change.

The ADP card under LMCache
LMCache unchanged · the plugin is its L2
vLLM LMCacheMPConnector in every rank LMCACHE SERVER · UNCHANGED L1 · CPU tier pinned host memory index, eviction policy L2 adapter type native_plugin loads the ADP plugin ADP L2 plugin C++, no Python lock Storage gateway fragments, databases ADP card NVMe SSDs behind it batches ring + control port STORE objects leaving L1 go to the card LOAD L1 hit: host memory otherwise, and after a restart: card → L1 → GPU

Use it when LMCache is already part of your stack. Otherwise use the OffloadingSpec backend: it needs no host-memory tier (comparison).

How it stores data

  • Objects and fragments. One LMCache chunk is one object. The plugin cuts it into fragments of fragment_bytes, and each fragment is one I/O. The plugin writes a small manifest after the card acknowledged every fragment. An object is on the card only when its manifest is there.
  • Integrity. A load checks the manifest: size, geometry and key. By default it also checks a sample checksum of every fragment. A mismatch is a miss for that object only. The plugin removes a manifest that it cannot use, so the next store writes the object again.
  • No double writes. Before a store, the plugin asks the card for the object. When the object is there, the plugin skips the store. A key is a hash of the tokens, so the same chunk is the same entry.
  • Failures. A missing key or fragment is a miss. When the gateway stops to answer (wait_timeout_s), every answer is a miss until the plugin opens a new session (reopen_after_s). This is also how the plugin finds a restarted gateway, or a gateway that was not up at the start of the server.
  • Backpressure. When the store backlog is above max_pending_store_mb, the plugin refuses new stores at once. A slow card then does not hold L1 objects. Loads have priority over stores.

Requirements

vLLM0.28.0
LMCache0.5.4 (in the vllm/vllm-openai:v0.28.0 image) or 0.5.5, in multiprocess (server) mode.
ImagevLLM 0.28.0 with the plugin compiled on it, delivered by Awide Labs on request
Gateway and cardas for the other integrations: ADP card setup, the gateway from Installation

Run it

The LMCache server takes the plugin as its L2 adapter:

lmcache server --host 127.0.0.1 --port 6555 --l1-size-gb 120 --eviction-policy LRU --chunk-size 256 \
  --l2-adapter '{"type":"native_plugin","module_path":"xkv_lmcache","class_name":"XkvL2Connector",
                 "adapter_params":{"port":5557,"fragment_bytes":131072,"affinity_chunks":1}}'

vLLM connects to the LMCache server:

--kv-transfer-config '{"kv_connector":"LMCacheMPConnector",
  "kv_connector_module_path":"lmcache.integration.vllm.lmcache_mp_connector",
  "kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.port":6555}}'

Inside the vLLM container. With LMC_ENABLE=1, the main process of the container starts the LMCache server and waits until its port answers. The server and its L1 then restart with vLLM.

LMC_ENABLE=1 LMC_L1_GB=120 LMC_CHUNK=256 LMC_XKV_PORT=5557 LMC_XKV_FRAG=131072
# optional: LMC_XKV_VERIFY, LMC_XKV_COPY_THREADS, LMC_XKV_STATS_PATH=/tmp/xkv-stats.json,
#           LMC_XKV_PARAMS='{"batch_slots":2048}' (merged last); python3 -m xkv_lmcache_autostart --help lists them all

As a separate process. Then L1 stays after a restart of vLLM. Use the same image and start python3 -m xkv_lmcache_autostart --exec (without LMC_ENABLE). The process needs:

  • the GPUs, because it copies L1 to the GPU through CUDA IPC
  • the IPC namespace of the host, and the same /dev/shm as vLLM and the gateway
  • the gateway port
docker run -d --name lmcache-server --gpus all --network host --ipc host \
  -e LMC_HOST=0.0.0.0 -e LMC_PORT=6555 -e LMC_L1_GB=120 -e LMC_CHUNK=256 \
  -e LMC_XKV_PORT=5557 -e LMC_XKV_HOST=<gateway address> -e LMC_XKV_FRAG=131072 \
  --entrypoint python3 <image> -m xkv_lmcache_autostart --exec
# vLLM: "kv_connector_extra_config": {"lmcache.mp.host": "<server address>", "lmcache.mp.port": 6555}

On Kubernetes, run it as a second container in the model pod, or as a pod with host network and host IPC on the node.

Settings

Set the plugin in the adapter_params of the L2 adapter, or with the LMC_XKV_* variables of the autostart. Each key and its default is in Configuration → LMCache plugin. Set fragment_bytes to the I/O size of the card: 131072 on a remote card, 261120 on a local card. Keep the L2 limit of LMCache (max_capacity_gb) at 0. The key eviction of the card manages the space on the card.

affinity_chunks (default 1, or the variable LMC_XKV_AFFINITY_CHUNKS of the autostart) sets how many LMCache chunks at the start of the prompt make the dialog mark. All chunks of one dialog go to one card (how a dialog finds its card).

What to expect

  • Hits in L1 come back a little faster than from the card, but L1 takes host memory.
  • Hits from the card go from the card to L1 and then to the GPU. LMCache completes the whole prefetch into L1 before it copies to the GPU. So a restore after a restart of the engine is slower than through the other integrations. This cost is inside LMCache, not in the plugin.
  • Host memory. The data goes through two host buffers: the gateway ring and L1.

Limits:

  • one LMCache server for each node, and one gateway for each server
  • two servers must not write to one card
  • Linux on x86_64 only

Monitoring

Every stats_interval_s, the plugin writes a summary line with the prefix [xkv-lmcache] to the log of the LMCache server. stats_path writes the full JSON: tasks for each operation, hits, bytes, latency percentiles, queue depths, ring use, integrity counters, session state and process memory. These counters show the result of the stores:

CounterMeaning
store_skippedThe object was already on the card.
store_dedupedThe plugin removed a duplicate store.
store_droppedThe backlog was full.
store_rejected_by_cardThe card refused the write. When it grows, check the capacity of the card.

Planning a deployment?

Talk to our engineers about your models, context lengths, concurrency and where the ADP card should sit.

Talk to Awide Labs