RAGFS 缓存
RAGFS 缓存是 OpenViking 的可选读缓存层,用于加速文件全量读取和目录读取。它只作为加速层,不作为事实数据源;数据仍以 backend filesystem 为准。
适用前提:
- 只有一个 OpenViking / RAGFS 进程写入同一 namespace。
- 文件和目录变更都经过 RAGFS。
- backend 不被外部绕过 RAGFS 直接修改。
- 缓存 Provider 的同一 key 写入或删除成功后,后续读取不会返回旧值。
快速开始
首次配置仍建议先完成基础配置:
openviking-server init
openviking-server doctor然后在 ~/.openviking/ov.conf 的 storage.agfs.cache 中启用缓存。下面是 Redis 示例,适合快速验证:
{
"storage": {
"workspace": "./data",
"agfs": {
"backend": "local",
"cache": {
"enabled": true,
"provider": "redis",
"namespace": "openviking",
"max_file_size_bytes": 1048576,
"bypass_prefixes": ["/queue", "/tmp"],
"redis": {
"mode": "standalone",
"endpoints": ["redis://127.0.0.1:6379"],
"pool_size": 32,
"connect_timeout_ms": 1000,
"command_timeout_ms": 20,
"key_prefix": "ragfs-cache",
"default_ttl_seconds": 3600,
"read_from_replica": false
}
}
}
}
}启动 Redis 和 OpenViking:
redis-server
openviking-server --config ~/.openviking/ov.conf如果配置文件在默认路径 ~/.openviking/ov.conf,也可以直接运行:
openviking-server可用 Provider:
| Provider | 适用场景 | 备注 |
|---|---|---|
memory | 本地验证、测试 | 进程内缓存,重启后丢失 |
redis | 快速落地、普通网络环境 | 当前支持 standalone;建议只从 primary 读取 |
yuanrong | 近计算缓存、共享内存或异构多级缓存 | 需要 Yuanrong worker 和 native feature |
mooncake | 远程内存池、RDMA/TCP 数据面 | 需要 Mooncake 服务和 native feature |
如果运行包没有编译对应 Provider,启动时会返回类似 “requires the ... feature” 的错误。
原生 Provider 构建
标准 OpenViking wheel 适用于 memory 和 redis Provider。yuanrong 和 mooncake Provider 依赖平台相关的原生 SDK,需要针对目标部署环境单独构建。
先安装 wheel 构建工具:
python -m pip install "maturin[patchelf]"Yuanrong
安装 Yuanrong DataSystem C++ SDK,并导出头文件和库目录:
export YUANRONG_SDK_INCLUDE=/path/to/yuanrong/include
export YUANRONG_SDK_LIB_DIR=/path/to/yuanrong/lib
# 可选,默认值为 "datasystem"。
export YUANRONG_SDK_LIB_NAME=datasystem
export LD_LIBRARY_PATH="$YUANRONG_SDK_LIB_DIR:${LD_LIBRARY_PATH:-}"构建并安装 wheel:
maturin build --release \
--manifest-path crates/ragfs-python-native/Cargo.toml \
--features yuanrong-native
python -m pip install --force-reinstall target/wheels/ragfs_python-*.whlOpenViking 启动时,storage.agfs.cache.yuanrong 配置的 Yuanrong worker 必须可用。
Mooncake
检出 crates/ragfs-cache-mooncake/Cargo.toml 使用的 Mooncake revision, 然后构建启用 Rust 支持的 Mooncake Store:
cmake -S /path/to/Mooncake -B /path/to/Mooncake/build \
-DWITH_STORE=ON \
-DWITH_STORE_RUST=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build /path/to/Mooncake/build \
--target build_mooncake_store_rust mooncake_master -j导出 Mooncake 官方 Rust binding 所需路径:
export MOONCAKE_BUILD_DIR=/path/to/Mooncake/build
export MOONCAKE_STORE_LIB_DIR="$MOONCAKE_BUILD_DIR/mooncake-store/src"
export MOONCAKE_STORE_INCLUDE_DIR=/path/to/Mooncake/mooncake-store/include
export LD_LIBRARY_PATH="$MOONCAKE_BUILD_DIR/mooncake-common:\
$MOONCAKE_BUILD_DIR/mooncake-common/src:\
$MOONCAKE_BUILD_DIR/mooncake-store/src:\
$MOONCAKE_BUILD_DIR/mooncake-store/src/cachelib_memory_allocator:\
$MOONCAKE_BUILD_DIR/mooncake-transfer-engine/src:\
$MOONCAKE_BUILD_DIR/mooncake-transfer-engine/src/common/base:\
${LD_LIBRARY_PATH:-}"构建并安装 wheel:
maturin build --release \
--manifest-path crates/ragfs-python-native/Cargo.toml \
--features mooncake-native
python -m pip install --force-reinstall target/wheels/ragfs_python-*.whlOpenViking 启动时,storage.agfs.cache.mooncake 配置的 Mooncake metadata service 和 Master 必须可用。原生 wheel 与平台相关,应在与目标部署环境兼容的 系统中构建。
生产 wheel 应使用仅在显式启用 ASan 时才链接 libasan 的 Mooncake revision。 构建后检查 release wheel 不包含且不依赖 libasan:
rm -rf /tmp/ragfs-python-wheel
python -m zipfile -e target/wheels/ragfs_python-*.whl /tmp/ragfs-python-wheel
readelf -d /tmp/ragfs-python-wheel/ragfs_python/ragfs_python.abi3.so \
| grep libasan
find /tmp/ragfs-python-wheel -name 'libasan*'两项检查都应无输出。
配置项
storage.agfs.cache 支持以下通用配置:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enabled | bool | false | 是否启用 RAGFS 缓存 |
provider | str | "memory" | memory、redis、yuanrong 或 mooncake |
namespace | str | "openviking" | 缓存命名空间,用于隔离不同部署或租户 |
max_file_size_bytes | int | 1048576 | 允许进入缓存的最大完整文件大小 |
bypass_prefixes | list[str] | [] | 强制绕过缓存的路径前缀 |
Redis 配置:
| 参数 | 默认值 | 说明 |
|---|---|---|
mode | "standalone" | Redis 部署模式 |
endpoints | ["redis://127.0.0.1:6379"] | Redis 连接地址 |
username | "" | Redis ACL 用户名 |
password_env | "" | 存放 Redis 密码的环境变量名 |
pool_size | 32 | 命令并发数 |
connect_timeout_ms | 1000 | 连接超时 |
command_timeout_ms | 20 | 命令超时 |
key_prefix | "ragfs-cache" | Redis 侧 key 前缀 |
default_ttl_seconds | 3600 | 默认 TTL;0 表示不设置 TTL |
read_from_replica | false | standalone 模式下必须为 false |
Yuanrong 配置:
{
"storage": {
"agfs": {
"cache": {
"enabled": true,
"provider": "yuanrong",
"yuanrong": {
"host": "127.0.0.1",
"port": 31501,
"connect_timeout_ms": 5000,
"request_timeout_ms": 5000,
"sdk_concurrency": 4
}
}
}
}
}Mooncake 配置:
{
"storage": {
"agfs": {
"cache": {
"enabled": true,
"provider": "mooncake",
"mooncake": {
"local_hostname": "127.0.0.1",
"metadata_server": "http://127.0.0.1:8080/metadata",
"master_server_addr": "127.0.0.1:50051",
"protocol": "tcp",
"device_name": "",
"global_segment_size": 536870912,
"local_buffer_size": 134217728,
"replica_num": 2,
"sdk_concurrency": 4,
"operation_timeout_ms": 5000
}
}
}
}
}整体架构
RAGFS 将缓存拆成两层:
CachedFileSystem:实现文件系统语义,包括 cache hit/miss、backend 回源、回填、失效、generation 校验和指标。CacheProvider:只负责缓存对象的get、put、delete、批量读写和关闭。
调用关系:
OpenViking
-> RAGFS / MountableFS
-> CachedFileSystem
|-> CacheProvider -> Memory / Redis / Yuanrong / Mooncake
`-> Backend FileSystem这种边界让文件、目录、rename、递归删除和写后失效逻辑只在公共层实现。Provider 不需要理解路径语义,只需要稳定存取 key-value 对象。
缓存对象
RAGFS 主要缓存三类对象。
文件缓存
文件 key 使用稳定命名空间和路径 hash:
ragfs:v1:{namespace}:file:{hash(path)}文件 value 是 CacheEnvelope,包含文件内容、对象类型、路径和 generation 快照。全量读取命中后,RAGFS 会先校验 envelope 和 generation,再返回内容。
默认策略会优先缓存 .abstract.md 和 .overview.md 这类摘要文件;超过 max_file_size_bytes 的文件不会进入缓存。非全量 range read 也会绕过缓存。
目录缓存
目录 key:
ragfs:v1:{namespace}:dir:{hash(path)}目录缓存保存 backend 原始 read_dir entries,而不是权限过滤后的最终结果。权限、角色和 agent context 仍在 OpenViking 上层实时处理。
这样同一份目录缓存可以服务 ls、tree、glob、grep 的文件收集阶段,以及删除或移动前的路径收集。
子树 Generation
子树 generation key:
ragfs:v1:{namespace}:subtree:{hash(scope)}remove_all 和目录 rename 可能让 Provider 中残留子孙 key。RAGFS 通过 bump subtree generation,让旧 envelope 的 generation 快照失效,后续真实读取会回源并重建缓存。
一致性与失效
单写者场景下,RAGFS 不需要分布式写锁。关键是按文件系统语义维护三类失效:
- 文件变更:删除或更新
file_key(path),删除dir_key(parent)。 - 目录变更:删除目录自身和父目录的
dir_key。 - 子树变更:对递归删除和目录 rename bump
subtreegeneration。
典型写入顺序:
获取进程内操作锁
-> 执行 backend 变更
-> 更新或删除相关 cache key
-> 必要时 bump subtree generation
-> 返回结果如果 Provider 失败,RAGFS 会以 backend 为准,并让受影响路径进入短期 bypass,避免继续读取可能陈旧的缓存。
请求合并
当多个请求同时读取同一个未缓存的小文件或目录时,CachedFileSystem 会用进程内 inflight 表合并请求:
第一个 miss 请求成为 leader,负责回源和回填。
后续相同 key 的请求成为 follower,等待 leader 结果。
请求完成后删除 inflight 条目。这只减少同一 OpenViking 进程内的重复 backend 访问,不改变 Provider 的一致性边界。
缓存策略
RAGFS 会自动绕过不适合缓存的路径:
- 锁文件:
.path.ovlock、*.lock、*.lck - 控制文件:
enqueue、dequeue、peek、ack - 瞬时状态:
heartbeat、lease、cursor、offset、pid - 用户通过
bypass_prefixes指定的路径前缀
权限敏感目录建议加入 bypass_prefixes。如果目录原始 entries 本身就依赖调用者权限,不应缓存该目录。
故障与观测
缓存层不能影响文件系统正确性:
get失败:回源 backend。put失败:记录错误,路径进入 bypass。delete失败:记录错误,路径或 scope 进入 bypass。- Provider 不可用:不返回旧缓存,以 backend 结果为准。
建议重点观察:
- cache hit / miss / bypass
- stale generation
- provider get / put / delete 延迟
- cache set / delete 失败
- inflight leader / follower / backend saved
- backend fallback 字节数
推荐使用顺序
- 本地用
memory验证配置形态。 - 用
redis验证真实远程缓存收益。 - 对高性能环境再接入
yuanrong或mooncake。 - 先缓存摘要文件和 raw
read_dir,再扩展到更多普通小文件。 - 将锁、控制面和权限敏感路径加入
bypass_prefixes。
一句话总结:RAGFS 缓存负责“按文件系统语义正确失效”,Provider 负责“把缓存对象放在哪里”。只要 backend 是事实来源,缓存命中就必须先通过 envelope 和 generation 校验。
