Skip to content

Commit 82d96fe

Browse files
authored
Merge pull request #44 from kobe0938/lmcache-backend
2025-09-11-extending-lmcache-backends.md
2 parents 561603d + 9b83e72 commit 82d96fe

4 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
layout: post
3+
title: "Extending LMCache Backends: A Comprehensive Guide to Custom Backend Development"
4+
subtitle: "Learn how to build custom backends for LMCache using the external backend extension mechanism"
5+
tags: [backend, extension, customization, storage, lmcache]
6+
comments: true
7+
author: Baolong, Kobe
8+
---
9+
10+
## Abstract
11+
12+
In large language model inference scenarios, the performance and flexibility of KVCache caching systems directly impact overall service efficiency. LMCache, as a high-performance large model caching framework, provides developers with rich extension capabilities through its modular backend design. This article will start with LMCache backend's extension mechanism, using the officially provided `lmc_external_log_backend` as an example, to detail how to customize backends that meet business requirements, including design principles, implementation details, and practical steps. We hope that various vendors can easily extend their own backends based on this guide.
13+
14+
## LMCache's Existing Backends
15+
16+
<div align="center">
17+
<img src="/assets/img/lmcache_backend.png" alt="LMCache Backend Overview" style="width: 90%; vertical-align:middle;">
18+
<p><em>LMCache Backend Architecture Overview</em></p>
19+
</div>
20+
21+
As shown in the diagram above, LMCache currently provides numerous backends that serve as storage and transmission adapters for KVCache.
22+
23+
Among them, `LocalCpuBackend` is the most commonly used backend and is essential in most cases, as it not only serves as a hot cache but also takes responsibility for allocating the necessary MemoryObjects for storage and retrieval processes.
24+
25+
Another special one is `NixlBackend`, whose purpose is to transmit KVCache to peer nodes rather than sending it to local or remote storage.
26+
27+
In addition, `RemoteBackend` is currently the most easily extensible backend, adopting the Connector framework, which allows storage providers to integrate with LMCache more conveniently.
28+
29+
Finally, the highlighted `ExternalBackend` is the dynamically extensible external backend that this article will introduce in detail.
30+
31+
## Why Extend LMCache Backends?
32+
33+
LMCache provides common backends based on memory, local disk, GDS, etc., by default. However, in actual production environments, developers may face the following customization needs:
34+
35+
- **Special storage medium adaptation**: Need to integrate with distributed storage (such as Ceph), non-volatile memory (NVM), and other special media
36+
- **Customized persistence strategies**: Need to implement specific persistence logic such as write-ahead logging (WAL) or snapshot-based approaches to ensure data reliability
37+
- **Business-level monitoring and auditing**: Need to embed business logic such as log reporting and performance statistics into cache operations
38+
- **Compatibility and compliance**: Need to adapt to internal storage protocols or meet requirements for data encryption, access control, and other compliance needs
39+
40+
`lmc_external_log_backend` can be viewed as an extension example designed for "logging" scenarios. It provides an excellent reference for understanding the extension mechanism by implementing LMCache's abstract interface and recording cache operations in log format.
41+
42+
## Core Mechanism of LMCache Backend Extension
43+
44+
LMCache adopts an "abstract interface + concrete implementation" design pattern, where all backends must follow unified interface specifications to ensure decoupling from the framework's core logic. Before starting development, you need to master the following core concepts:
45+
46+
### Core Abstract Interface Definition
47+
48+
LMCache defines the general interface in `StorageBackendInterface` in `lmcache/v1/storage_backend/abstract_backend.py`.
49+
50+
<div align="center">
51+
<img src="/assets/img/StorageBackendInterface.png" alt="StorageBackendInterface Structure" style="width: 85%; vertical-align:middle;">
52+
<p><em>StorageBackendInterface Class Structure</em></p>
53+
</div>
54+
55+
The core methods that extended backends need to implement include:
56+
57+
| Interface Method | Function Description |
58+
|------------------|---------------------|
59+
| `__init__(self, config, metadata, loop, memory_allocator, local_cpu_backend: LocalCPUBackend, dst_device, lookup_server=None)` | Initialize the backend (such as opening files, connecting to storage). The constructor should refer to the latest definition in LMCache's `lmcache/v1/storage_backend/__init__.py` in the `create_dynamic_backends` method to maintain consistency |
60+
| `contains(self, key: CacheEngineKey, pin: bool = False) -> bool` | Check if the specified key exists |
61+
| `exists_in_put_tasks(self, key: CacheEngineKey) -> bool` | Check if the key is in asynchronous put tasks |
62+
| `batched_submit_put_task(self, keys: Sequence[CacheEngineKey], objs: List[MemoryObj], transfer_spec=None)` | Submit a batch of put tasks at once |
63+
| `submit_prefetch_task(self, key: CacheEngineKey) -> bool` | Submit prefetch tasks |
64+
| `get_blocking(self, key: CacheEngineKey) -> Optional[MemoryObj]` | Get key content in blocking mode |
65+
| `get_non_blocking(self, key: CacheEngineKey) -> Optional[Future]` | Get key content asynchronously, returning a Future object for waiting for data content |
66+
| `batched_get_blocking(self, keys: List[CacheEngineKey]) -> List[Optional[MemoryObj]]` | Get a batch of key contents in blocking mode at once |
67+
| `pin(self, key: CacheEngineKey) -> bool` | Pin the MemoryObject corresponding to this key to prevent eviction |
68+
| `unpin(self, key: CacheEngineKey) -> bool` | Unpin this key so it can be evicted |
69+
| `remove(self, key: CacheEngineKey, force: bool = True) -> bool` | Delete this key |
70+
| `batched_remove(self, keys: list[CacheEngineKey], force: bool = True) -> int` | Delete a batch of keys at once, returning the actual number of deleted objects |
71+
72+
Any custom backend only needs to inherit the `StorageBackendInterface` abstract class and implement the above interfaces to seamlessly integrate into LMCache.
73+
74+
### Process for Using Extended Backends
75+
76+
LMCache loads custom backends through the following process, achieving decoupling through "develop extension - install extension - configure extension":
77+
78+
1. **Develop Extension**: Inherit `StorageBackendInterface`, reference `lmc_external_log_backend`, implement interface functions, then package into a wheel package
79+
2. **Install Extension**: Install the extension locally via `pip install <backend whl>`
80+
3. **Configure Extension**: Users specify `external_backends: "<new extension backend>"` in the LMCache configuration file, such as specifying `log_external_backend` as the self-extended backend. You can also provide comma-separated values to specify multiple external backends
81+
82+
Additionally, you need to configure `extra_config` to specify additional configuration for custom external extensions.
83+
84+
Here's a configuration example using `log_external_backend`:
85+
86+
```yaml
87+
chunk_size: 64
88+
local_cpu: False
89+
max_local_cpu_size: 5
90+
external_backends: "log_external_backend"
91+
extra_config:
92+
external_backend.log_external_backend.module_path: lmc_external_log_backend.lmc_external_log_backend
93+
external_backend.log_external_backend.class_name: ExternalLogBackend
94+
```
95+
96+
## Custom External Backend Example: lmc_external_log_backend Introduction
97+
98+
`lmc_external_log_backend` is an official backend example provided by LMCache. Its function is the simplest implementation of necessary interfaces that prints logs. Its core goal is to demonstrate the backend extension approach.
99+
100+
<div align="center">
101+
<img src="/assets/img/ExternalLogBackend.png" alt="ExternalLogBackend Implementation" style="width: 80%; vertical-align:middle;">
102+
<p><em>ExternalLogBackend Class Implementation Structure</em></p>
103+
</div>
104+
105+
As shown in the diagram above, `ExternalLogBackend` inherits from LMCache's `StorageBackendInterface` and implements the agreed-upon `__init__` method and other abstract methods.
106+
107+
### 1. Backend Initialization
108+
109+
```python
110+
class ExternalLogBackend(StorageBackendInterface):
111+
def __init__(
112+
self,
113+
config,
114+
metadata,
115+
loop,
116+
memory_allocator,
117+
local_cpu_backend: LocalCPUBackend,
118+
dst_device,
119+
lookup_server=None,
120+
):
121+
super().__init__(dst_device=dst_device)
122+
self.config = config
123+
self.metadata = metadata
124+
self.loop = loop
125+
self.memory_allocator = memory_allocator
126+
self.lookup_server = lookup_server
127+
logger.info(f"ExternalLogBackend initialized for device {dst_device}")
128+
```
129+
130+
### 2. Implementing Core Methods
131+
132+
Here are a few example methods; for complete implementation, please refer to the code repository:
133+
134+
```python
135+
def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
136+
logger.info(f"ExternalLogBackend Checking contains for key: {key}")
137+
return False
138+
139+
def batched_submit_put_task(
140+
self,
141+
keys: List[CacheEngineKey],
142+
objs: List[MemoryObj],
143+
transfer_spec=None,
144+
) -> Optional[List[Future]]:
145+
for key, obj in zip(keys, objs):
146+
# Record storage operation for each key
147+
logger.info(f"ExternalLogBackend Put task for key: {key}, size: {obj.tensor.size()}")
148+
return None
149+
150+
def submit_prefetch_task(self, key: CacheEngineKey) -> bool:
151+
logger.info(f"ExternalLogBackend Prefetch task for key: {key}")
152+
return True
153+
```
154+
155+
### 3. Complete Implementation Reference
156+
157+
This log backend doesn't actually store data but records all operations to the logging system. For complete code, please refer to the GitHub repository [lmc_external_log_backend](https://github.com/opendataio/lmc_external_log_backend). The code repository explains how to package, create wheel packages, and install them, which this article won't elaborate on.
158+
159+
## Summary
160+
161+
LMCache's external backend extension mechanism provides developers with powerful customization capabilities:
162+
163+
1. **Flexible Integration**: Supports various storage systems
164+
2. **Non-intrusive**: No need to modify existing LMCache repository code
165+
3. **Dynamic Loading**: New backends can be enabled through dependency installation and configuration only
166+
4. **Easy Development**: Clear interfaces and documentation support
167+
168+
Whether you need to add monitoring, support new hardware, or implement custom storage strategies, extending LMCache backends can efficiently achieve these goals. The `lmc_external_log_backend` project is an excellent starting point that can help you quickly understand the backend development process.

assets/img/ExternalLogBackend.png

136 KB
Loading
202 KB
Loading

assets/img/lmcache_backend.png

222 KB
Loading

0 commit comments

Comments
 (0)