本文档描述 Tair KVCache 各模块的职责,以及模块之间的关联关系(依赖方向、控制流、数据流)。目的是在修改或新增功能时,能快速判断当前模块受哪些模块约束、又会影响到哪些模块,避免遗漏关联模块带来的隐性约束。
维护提示:当模块的职责、依赖方向或调用关系发生变化,或新增/删除模块时,请同步更新本文档与文末的 Mermaid 图,并同步更新 AGENTS.md 中的缩略图。
相关文档:基本概念、ReportEvent Snapshot URI 版本方案、高可用与选主机制、CacheReclaimer 异步删除设计、后台扫描 GC 设计、配置指南、优化器文档。
仓库包含三个相对独立的部分:
| 部分 | 路径 | 说明 |
|---|---|---|
| KVCache Manager | kv_cache_manager/ |
核心系统:全局 KVCache 元数据管理服务,以及配套的客户端 SDK 与推理框架连接器。本文档的主体。 |
| HiSim | hisim/ |
独立的 LLM 推理仿真系统,通过回放 trace 预测 TTFT/TPOT/吞吐等指标,不依赖 Manager 运行时。 |
| Optimizer | kv_cache_manager/optimizer/ |
缓存仿真与优化:既支持离线回放 KVCache 访问 trace,也提供在线 TraceQuery 服务。完整 optimizer 用于策略/容量分析;LiteHit 用最小状态精确计算多容量 full-attention LRU 命中率。 |
KVCache Manager 采用中心化部署,负责 KVCache 的全局元数据管理(查询、写入、容量管理),推理引擎通过 Client/Connector 接入。
以下模块均位于 kv_cache_manager/ 下。
| 模块 | 目录 | 职责 |
|---|---|---|
| 入口 | main.cpp |
构造 CommandLine 并运行,唯一依赖 service。 |
| service | service/ |
接入层。Server 在启动时创建并串联几乎所有组件(整个服务的装配入口);*ServiceImpl(meta/admin/debug)实现与传输无关的业务入口,grpc_service/、http_service/ 是对应传输适配层,util/ 负责 proto↔领域对象转换、调用守卫与访问日志。 |
| manager | manager/ |
编排层与业务核心。CacheManager 是中心门面,对外提供注册实例、查询/写入/删除 Cache、上报事件、容量回收、后台 GC 与分层迁移等能力,并协调 MetaSearcher、WriteLocationManager、DataStorageSelector、CacheReclaimer、CacheGarbageCollector、MigrationManager、SchedulePlanExecutor 等子组件。 |
| meta | meta/ |
元数据平面。MetaIndexerManager 按 instance_id 管理 MetaIndexer,维护 cache key → CacheLocation 的索引;元数据后端可插拔;meta_search_cache 做查询缓存。CacheLocation 是被广泛共享的核心类型。 |
| config | config/ |
配置模型 + 注册表 + HA 协调层。定义各类配置对象;RegistryManager 持久化实例注册信息;CoordinationBackend + LeaderElector 提供一主多备的分布式选主。 |
| data_storage | data_storage/ |
可插拔的 KVCache 数据存储后端。DataStorageManager 管理后端集合,DataStorageBackend 抽象存储介质,DataStorageUri 统一位置描述。 |
| 模块 | 目录 | 职责 |
|---|---|---|
| common | common/ |
基础设施层:日志、JSON、错误码、Redis 客户端、RequestContext(逐请求追踪上下文)、concurrent_hash_map、lru_cache、loop_thread、服务发现、崩溃处理等。几乎所有 C++ 模块都依赖它。 |
| metrics | metrics/ |
可观测性。MetricsRegistry/MetricsCollector 收集指标,多种 reporter(kmonitor/local/logging/dummy)上报,PrometheusExporter 通过 HTTP 暴露。 |
| event | event/ |
轻量事件总线。EventManager 将领域事件分发给注册的 EventPublisher;除默认日志发布器外,OptimizerEventPublisher 会将缓存读取事件转换为 protocol 中的 TraceQueryRequest,再经 SubscriptionEventSink 交给 service 层的 gRPC 流。 |
| protocol | protocol/protobuf/ |
gRPC/proto 契约。定义 meta/admin/debug/kv_meta 以及 optimizer 事件流服务,生成 C++ 与 Python 桩。 |
| 模块 | 目录 | 职责 |
|---|---|---|
| client | client/ |
C++/Python 客户端 SDK,是推理引擎与 KVCM 之间的桥梁。对外提供 ManagerClient/RTPLLMClient 门面,内部由两条链路组成(见下):元数据面 MetaClient(经 gRPC 桩 internal/stub 调用 KVCM 服务)与数据面 TransferClient(经 internal/sdk 在推理引擎显存/内存与存储后端之间搬运 KVCache 数据)。面向外部,不被服务端核心调用。 |
| py_connector | py_connector/ |
推理框架集成(Python)。将 client 接入 vLLM/SGLang/TRT-LLM,含 CUDA kernel 辅助,负责在引擎的推理流程中按正确顺序调用元数据面与数据面接口。此外自带一个纯 Python 的 HTTP 元数据面客户端 KvCacheManagerClient(common/manager_client.py),可通过统一服务发现 URL 获取 Manager 入口,并作为 C++ MetaClient 之外的另一条元数据面通路。位于 Python 侧栈顶。 |
三个面的界定:本文档区分三个面——元数据面指 MetaService 的接口(
GetCacheLocation/StartWriteCache/FinishWriteCache/GetCacheMeta/RemoveCache/RegisterInstance等)及 client 侧调用这些接口的逻辑,是推理引擎读写 KVCache 的热路径;数据面指 KVCache 数据在引擎显存/内存与存储后端之间的实际搬运(TransferClient,不经过 KVCM);管控面仅指 AdminService 的接口(Storage 增删改、Instance Group 管理、账号、配置快照、运维监控、Leader 运维等),供运维/管理工具使用,不在推理引擎的读写热路径上。
client 覆盖元数据面与数据面两条链路,其对应关系如下(管控面由 AdminService 承载,不属于 client SDK 的常规链路):
| 链路 | 组件 | 依赖 | 对应 KVCM 服务端接口 |
|---|---|---|---|
| 元数据面 | MetaClient → internal/stub:grpc_stub |
protocol、config、service/util:manager_message_proto_util |
MetaService:GetCacheLocation / StartWriteCache / FinishWriteCache / GetCacheMeta / RemoveCache 等 |
| 数据面(数据搬运) | TransferClient → internal/sdk |
data_storage(URI/common_define)、common |
不经过 KVCM,直接读写存储后端 |
元数据面到 KVCM 有两条等价通路,最终都落到服务端同一套 *ServiceImpl(grpc_service/http_service 只是传输适配层):
- C++
MetaClient(gRPC):走internal/stub:grpc_stub,供 C++ 侧与经 pybind 的引擎使用。 - Python
KvCacheManagerClient(HTTP):位于py_connector/common/manager_client.py,用requests覆盖 MetaService 的全部/api/*端点(registerInstance/getInstanceInfo/getCacheMeta/getCacheLocation/getCacheLocationLen/getCacheLocationsByBackend/startWriteCache/finishWriteCache/removeCache/trimCache/getClusterInfo/reportEvent)。manager_uri可直接使用 HTTP(S) 地址,也可使用通用服务发现 URL;启用 Leader 发现后,客户端以动态发现的 Manager 端点调用/api/getClusterInfo,再根据leader_endpoint.meta_http_port直连 Leader,并处理SERVER_NOT_LEADER重试。普通 API 请求使用可配置的request_timeout_seconds(默认 1 秒),Leader 查询保留独立的 5 秒超时。不同连接器按需选用其一。
数据面则统一走 C++ TransferClient(经 pybind),与元数据面选哪条通路无关。
client 通过 InitParams.role_type 区分角色:SCHEDULER(调度节点)只创建 MetaClient 做元数据匹配与写地址申请;WORKER(推理节点)只创建 TransferClient 做数据搬运;HYBRID 两者都有。WORKER 的存储配置由 MetaClient::GetStorageConfig() 从 KVCM 下发获得,保证与服务端一致。
服务端核心是一条清晰的单向下降链:
service → manager → meta → config → data_storage → common
common、protocol是最底层的通用模块,被各层广泛依赖;event的 optimizer 发布链路直接使用protocol定义的TraceQueryRequest。metrics、event是通用支撑模块,被manager与service复用。service在启动时实例化CacheManager(注入MetricsRegistry+RegistryManager),并通过config的LeaderElector门控 recover/cleanup。
修改这两处时要特别小心,它们是有意为之的“向上依赖”,通过拆分细粒度 Bazel target 才避免了真正的循环依赖:
common:request_context→metrics:metrics_collector:RequestContext会直接采集指标,因此 common 反向依赖 metrics 的采集器 target。metrics:metrics_reporter→manager:cache_manager:reporter 需要读取实时 cache 状态,因此 metrics 反向依赖 manager。由于manager依赖的是metrics_registry/metrics_collector,而 reporter 位于独立的metrics_reportertarget,二者不构成 Bazel 环。
- client 是独立的对外分支,仅共享
common、config、data_storage、protocol以及service/util:manager_message_proto_util;py_connector 通过 pybind 位于 client 之上。核心服务端不依赖 client。运行时,元数据面经 gRPC(C++MetaClient)或 HTTP(py_connector 的 PythonKvCacheManagerClient)调用 KVCM 服务,数据面经 C++TransferClient直接读写存储后端——这几条链路是理解端到端流程的关键(见第 4 节)。 - optimizer 负责 KVCache 访问 trace 的仿真与优化(命中率/容量分析、逐出与容量参数调优),并通过独立 online runtime/service 提供实时 TraceQuery。full-attention LRU 的在线多容量统计复用 LiteHit;在线进程还可通过通用服务发现找到全部 KVCM endpoint,在 KVCM 的 Meta gRPC 端口调用
SubscribeEvents并直接回放事件。KVCM 不反向发现 Optimizer。
flowchart TD
main["main.cpp(入口)"]
subgraph access["接入层"]
service["service<br/>Server / *ServiceImpl<br/>grpc · http · util"]
end
subgraph core["编排与业务核心"]
manager["manager<br/>CacheManager + 子组件"]
end
subgraph dataplane["元数据 / 存储 / 配置"]
meta["meta<br/>MetaIndexer · CacheLocation"]
config["config<br/>Registry · LeaderElector · Coordination"]
data_storage["data_storage<br/>DataStorageManager · Backends"]
end
subgraph crosscut["通用支撑"]
metrics["metrics"]
event["event"]
protocol["protocol (proto/grpc)"]
common["common"]
end
subgraph clientside["客户端侧(对外分支)"]
client["client SDK"]
py_connector["py_connector<br/>vLLM/SGLang/TRT-LLM"]
end
optimizer["optimizer(仿真与优化)"]
%% 核心下降链(控制流)
main --> service --> manager --> meta --> config --> data_storage --> common
%% 通用支撑依赖
manager --> event
event --> protocol
manager --> metrics
service --> metrics
service --> event
service --> config
service --> data_storage
service --> protocol
manager --> protocol
event --> protocol
config --> protocol
meta --> config
meta --> data_storage
%% 有意的反向边(近似环,见 3.2)
common -. request_context .-> metrics
metrics -. metrics_reporter .-> manager
%% 客户端分支
py_connector --> client
client --> config
client --> data_storage
client --> protocol
client --> common
client -. proto 转换复用 .-> service
%% optimizer 的关联
optimizer -. cache_location 类型 .-> meta
optimizer --> common
optimizer --> protocol
optimizer -. gRPC SubscribeEvents(运行时) .-> service
%% 底层通用模块被广泛依赖
meta --> common
config --> common
data_storage --> common
metrics --> common
event --> common
图例:实线箭头表示“依赖 / 调用”;虚线箭头表示需要特别注意的反向边或弱耦合。
完整的端到端流程涉及三方:推理引擎(经 py_connector)、client(元数据面 + 数据面)、KVCM 服务端。关键点在于:元数据操作走元数据面到 KVCM,实际 KVCache 数据搬运走数据面直连存储后端,二者不混。KVCM 只管理“数据在哪、能不能读写”,不经手数据本身。元数据面到 KVCM 有两条等价通路——C++ MetaClient(gRPC)或 py_connector 的 Python KvCacheManagerClient(HTTP /api/*),下文以“元数据面”统称;数据面统一走 C++ TransferClient。
服务端内部流程都以 RequestContext 贯穿,指标采集在链路上逐层进行。HA 部署下只有 Leader 处理读写请求;client 先经 GetClusterInfo 发现 Leader 再直连(详见 ha_leader_elector.md)。
一次元数据面请求进入 KVCM 后:
client(MetaClient/gRPC)→ service(grpc 适配 → *ServiceImpl)
→ manager(CacheManager)→ meta(索引)/ data_storage(存储状态)→ common
推理引擎启动时经 client 注册实例,CacheManager::RegisterInstance 校验并落库实例配置(block_size、location spec、模型部署等)到 RegistryManager(config),并在 MetaIndexerManager 中为该 instance_id 建立索引。约束:KVCache 仅在同一 instance_id 内复用,跨 Instance 不匹配。
从完整视角看,读取由推理引擎驱动,client 的两条链路依次参与:
- 匹配:引擎经
ManagerClient::MatchLocation(元数据面)调用 KVCMCacheManager::GetCacheLocation(sByBackend)。KVCM 内部由MetaSearcher经meta_search_cache与MetaIndexer查得CacheLocation,DataStorageSelector依据后端可用性/水位选出返回哪个存储位置,返回一组存储位置 URI(Locations)。支持前缀匹配、滑动窗口匹配、批量匹配等查询类型。 - 加载:引擎拿到 URI 后,经
ManagerClient::LoadKvCaches(数据面TransferClient)由对应 SDK 从存储后端把 KVCache 数据读入引擎显存/内存。此步不经过 KVCM。
为保证数据可靠性,写入分两阶段,同样由引擎驱动、两条链路配合:
- 申请写地址(StartWriteCache):引擎经
ManagerClient::StartWrite(元数据面)调用 KVCMCacheManager::StartWriteCache。KVCM 过滤掉已存在的 block,经DataStorageSelector选择存储后端,由WriteLocationManager生成写入地址,返回write_session_id与写入位置 URI,对应CacheLocation进入writing态。 - 写入数据(SaveKvCaches):引擎经
ManagerClient::SaveKvCaches(数据面TransferClient)把 KVCache 数据写入返回的存储位置。此步不经过 KVCM。 - 确认(FinishWriteCache):引擎经
ManagerClient::FinishWrite(元数据面)回调 KVCM,CacheManager依据success_block_mask将成功的CacheLocation置为serving并写入元数据索引;失败的 block 不会转正,保证只有真正写成功的数据可被后续读取命中。
sequenceDiagram
participant E as 推理引擎<br/>(py_connector)
participant M as 元数据面<br/>(MetaClient/gRPC<br/>或 Python/HTTP)
participant T as TransferClient<br/>(数据面)
participant K as KVCM 服务端
participant S as 存储后端
Note over E,S: 读取流程
E->>M: 匹配 (MatchLocation / getCacheLocation)
M->>K: GetCacheLocation (gRPC 或 HTTP)
K-->>M: Locations (URIs)
M-->>E: 命中的 URI
E->>T: LoadKvCaches(URIs, 显存 buffers)
T->>S: 读取 KVCache 数据
S-->>E: 数据载入显存
Note over E,S: 写入流程(两阶段)
E->>M: 申请写地址 (StartWrite / startWriteCache)
M->>K: StartWriteCache (gRPC 或 HTTP)
K-->>M: write_session_id + 写入 URI
M-->>E: 写地址
E->>T: SaveKvCaches(URIs, 显存 buffers)
T->>S: 写入 KVCache 数据
S-->>E: 写入完成 (success_mask)
E->>M: 确认 (FinishWrite / finishWriteCache)
M->>K: FinishWriteCache (gRPC 或 HTTP)
K-->>E: 确认 (serving)
CacheReclaimer 依据 Quota 与存储水位选出待逐出的 key,并把 Location 删除作为端到端异步任务提交给 SchedulePlanExecutor。Executor worker 完成元数据 Get/CAS/Sync;Sync 成功后通过定时队列等待删除 delay(等待不占 worker),随后删除 data_storage 数据并 CAD meta 索引。Reclaimer 在任务终态前按 Instance Group 与 BaseStorageType 维护 pending Location、删除 bytes credit 和硬配额,用于去重、避免过度逐出及提供有界反压;回收动作通过 event 上报。完整生命周期和异常语义见 CacheReclaimer 异步删除设计。
分层迁移由 CacheReclaimer 根据 migration strategy 的 source storage 类型水位触发。Reclaimer cron 线程只完成 LRU 候选采样、同轮回收准入和异步 Job 构造,不在 cron 线程执行可能较慢的 Backend Create、最新 Location 查询或 Copy:
- 同一轮先执行 Reclaim 准入。删除请求被 Executor 接受后,Reclaimer 会同步把精确的
(instance_id, block_key, location_id)记入pending_locations_。 - 再构造 Migration Prepare Job。Job 携带候选 block 以及按 block 组织的 pending Location 快照,并完全持有跨异步边界所需的输入;Executor worker 不直接访问 Reclaimer 的可变状态。
MigrationManagerworker 重新读取当前 Instance、配置、strategy 和 Location,排除快照中的待删除 Location,再执行 Backend Create 以及统一的 Copy/Mark 准入与分发。配置已删除、Leader generation 已变化或 lifecycle gate 已关闭时,旧 Job 直接失效。- Copy 完成后再次校验精确 source Location 仍为
SERVING且 create time 未变化;若跨轮回收已经删除或替换 source,则不提升目标 Location,并清理本次生成的目标。
上述顺序只保证“同轮先做 Reclaim 准入”,不等待物理删除完成;当水位只达到 migration threshold 而未达到 reclaim threshold 时,迁移仍可独立触发。Admin 发起的迁移保持同步 Prepare 语义,但与 Reclaimer 路径复用 MigrationManager 的统一分发、活跃任务表和 Instance Group Copy 并发硬限制。
Reclaim、系统任务和 Migration 共用进程级 SchedulePlanExecutor。ready task 按 Reclaim → System → Migration Continuation → Migration Prepare 的顺序选择;Prepare、Copy 和迁移 cleanup 共同受 migration worker budget 约束。budget 必须小于线程池总大小,以保证长时间 Backend Create/Copy 不能占满所有 worker,至少有一个 worker 不会被 Migration 占用。任务优先级只影响尚未运行的任务,不能抢占已经执行的任务。参数约束和默认值见配置指南。
Migration 的背压分两层:活跃 Copy 数与 queued/running Prepare 数用于 Reclaimer 的跨轮提前剪枝,MigrationManager::BatchSubmit 再对 Instance Group Copy 并发做原子硬准入。同一 generation + instance group + instance + source + target 最多存在一个 queued/running Prepare,避免 Backend Create 变慢时 cron 重复堆积同一路由。Copy 路径还会在目标 CLS_WRITING 对 Reclaimer 可见前写入 active-task reservation,防止孤儿清理误删正在准备或执行的目标。
flowchart LR
subgraph cron["CacheReclaimer cron(单线程状态)"]
sample["采样 / LRU 候选"]
reclaim["Reclaim 准入"]
pending["pending_locations_"]
snapshot["按 block 复制 pending 快照"]
sample --> reclaim
reclaim --> pending
sample --> snapshot
pending --> snapshot
end
subgraph executor["共享 SchedulePlanExecutor"]
reclaim_task["kReclaim<br/>Get / CAS / Sync / Delete"]
prepare["kMigrationPrepare<br/>fresh 校验 / Backend Create / 分发"]
continuation["kMigrationContinuation<br/>Copy / cleanup"]
end
migration["MigrationManager<br/>统一 Copy / Mark 准入与活跃任务表"]
monitor["MigrationManager monitor<br/>完成校验 / promote / cleanup"]
reclaim -->|优先入队| reclaim_task
snapshot -->|Async Prepare Job| prepare
prepare --> migration
migration --> continuation
continuation --> monitor
migration -->|Mark| meta["MetaIndexer"]
migration -->|Create target| storage["DataStorageBackend"]
monitor --> meta
monitor --> storage
reclaim_task --> meta
reclaim_task --> storage
CacheGarbageCollector 只在 Leader 上运行,复用公共 LoopThread,按 Registry 快照和 backend cursor 串行扫描 authoritative metadata;扫描协调不占用共享的删除 worker。维护扫描不更新在线 LRU/revisit,也不向 local hot cache 回填。V1 识别超过 grace、且不属于活跃 Migration Copy 目标的 CLS_WRITING,并对普通 CLS_SERVING Location 按 storage 批量调用低成本 MightExist(),任一 spec 明确 missing 时选择整个 Location;EventReport 和探测不确定项均跳过。两类候选都通过 SchedulePlanExecutor::SubmitAsync 提交扫描时的完整序列化 Location,由 Executor worker 重新读取并以精确值条件 CAS 仲裁并发 Finish、Location 刷新和 Reclaimer。GC 使用固定小窗口管理在途 Future,并以 Instance-aware pending target 覆盖 accepted 到 CAS 的重复窗口;每 tick 最多提交一个请求,窗口满后停止扫描,完整 round 后进入长 cooldown。详细边界见 后台扫描 GC 设计。
LeaderElector(config)基于 CoordinationBackend(memory/file/redis)的分布式锁选主。Server 在成为 Leader 时调用 CacheManager::DoRecover 恢复状态,随后启动 GC、恢复 Reclaimer、启动 MigrationManager 并开放 leader-only 请求。降级时先通知 GC/Reclaimer 停止新工作并关闭、排空 leader-only 请求,再 join GC、停止 MigrationManager,最后调用 DoCleanup 清理运行时状态(正在进行的写入按失败处理)。Python KvCacheManagerClient 使用服务发现 URL 时,会在每次 Leader 刷新前重新选择一个 Manager 发现端点,避免把 Leader 查询入口固定在单个节点上。
CacheManager 在读取路径产生 CacheGetEvent,EventManager 将其同时交给各 publisher。启用 optimizer publisher 后,单 worker 按事件顺序转换为 TraceQueryRequest,再写入每个 gRPC 订阅者的独立有界队列。KVCM 的 OptimizerEventStreamService 注册在现有 Meta gRPC server 上,不新增端口。
Optimizer 作为客户端通过 ServiceDiscoveryFactory 获取 KVCM seed endpoint,再调用 MetaService.GetClusterInfo 定位当前 Leader;任意时刻只向 Leader 保持一条 SubscribeEvents response stream。supervisor 周期刷新服务发现和 Leader,并通过同一 Meta gRPC 端口上的 OptimizerEventStreamService.GetConfiguration 拉取 Instance Group / Instance 快照,按 Group 后 Instance 的顺序自动注册新增配置;未知 instance_id 会立即触发一次额外刷新。切主时先同步配置,再迁移 stream。stream 收到事件后直接调用 OnlineOptimizerManager::TraceQuery,Optimizer 侧不再增加业务队列。
修改某个模块前,先对照第 3 节的依赖关系图与第 4 节的运行时流程,确认它的上游(谁依赖它、会被它影响)和下游(它依赖谁、受谁约束),不要遗漏被依赖方带来的约束。尤其注意 3.2 的两条反向边、CacheLocation 的生命周期与状态流转约束,以及改动 protocol proto 时需遵循 proto 修改指南。