首页
/ OpenViking 多写存储(Multi-Write)实战指南:Primary + Backup 复制架构、S3 兼容后端与读加速配置

OpenViking 多写存储(Multi-Write)实战指南:Primary + Backup 复制架构、S3 兼容后端与读加速配置

2026-09-09 21:30:27作者:彭桢灵Jeremy

OpenViking 的多写存储(Multi-Write Storage)允许一个 primary 后端在统一文件系统抽象下同时向多个 backup 后端复制写入,用于高可用、跨区域副本、读加速和存储迁移。本文以 多写存储指南 为主线,结合 RAGFS 多后端源码 深入讲解从最小配置到生产级 S3 备份、同步/异步一致性选择、Redirect/Exclude 策略、加密与存量迁移的完整链路;读完本文,你将掌握在 ov.conf 中配置任意数量 backup、正确接入 S3 兼容服务、让备份参与读路由并排查常见故障的实战能力。

多写逻辑全部位于 RAGFS 内部:OpenViking 的 Python SDK、HTTP API 和 CLI 使用方式保持不变,read()write()ls()stat() 等接口语义不变,调用方无需关心文件最终落在哪个底层后端(参见 多写存储概念)。

核心模型:Primary 与 Backup

多写存储由一个 primary多个 backup 组成,角色与配置位置如下:

角色 配置位置 说明
primary storage.agfs.backend 权威写入目标,也是读取兜底
backup storage.agfs.backups.items[] 接收复制写入,可选参与读取

没有配置 backups 时,OpenViking 继续使用原有单后端模式——从源码看,PluginConfig.backups 字段的默认值是 Nonecrates/ragfs/src/core/types.rs),这正是"未配置即单后端"的实现依据。

默认写入路径为:

Client
  -> OpenViking API
  -> RAGFS MultiWrite
  -> primary
  -> backup1 / backup2 / ...

从源码结构看,多写核心是 MultiWriteWrappedFScrates/ragfs/src/core/multibackend_wrapper.rs):BackendEntry 中 primary 恒为索引 0,其余均为 backup,写入通过 execute_write 完成"primary 写入 → 写 sync 日志 → backup 扇出"的整条管线。backup 未配置 operations 时默认参与写入(participates_in_write()operations 为空时返回 true),这样用最少配置即可得到冷备能力。

前置条件

开始配置前确认以下事项:

  • 已有可用的 ov.conf
  • 已确认 primary backend 可以正常读写。
  • 如果要接入 S3 兼容存储,已准备好 bucket、endpoint 和访问凭据。
  • 如果要迁移已有数据,先完成存量数据迁移,再启用多写(多写只复制启用之后的新写入)。

最小配置:本地目录双写

下面示例使用本地目录作为 primary,并把写入复制到另一个本地目录:

{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "backups": {
        "sync_type": "async",
        "items": [
          {
            "name": "local-backup",
            "backend": "local",
            "local": {
                "workspace": "./data/backup"
            }
          }
        ]
      }
    }
  }
}

说明:

  • 顶层 backend 是 primary。
  • backups.items[] 是 backup 列表。
  • name 是 backup 的稳定身份,后续同步元数据会引用它;源码中 validate_redirect_targets 会逐一校验 redirect 的 target 是否能在 backup 列表中按 name 找到(crates/ragfs/src/multibackend/config.rs),找不到即报配置错误。
  • backend = "local" 的 backup 使用 local.workspace 指定本地目录。
  • sync_type 不配置时默认按异步模式理解——BackendsConfig.sync_type 的默认值在源码中定义为 "async"crates/ragfs/src/core/types.rs)。

多 Backup 配置

可以配置多个 backup。下面示例同时写入本地副本和 S3 兼容对象存储:

{
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "backups": {
        "sync_type": "async",
        "items": [
          {
            "name": "local-az2",
            "backend": "local",
            "local": {
                "workspace": "./data/local-az2"
            }
          },
          {
            "name": "object-store",
            "backend": "s3",
            "s3": {
              "bucket": "openviking-backup",
              "region": "us-east-1",
              "endpoint": "https://s3.example.com",
              "access_key": "your-access-key",
              "secret_key": "your-secret-key",
              "prefix": "openviking",
              "directory_marker_mode": "none"
            }
          }
        ]
      }
    }
  }
}

建议:

  • name 不要使用会频繁变化的机器名或临时编号。
  • backup 的底层路径或 bucket 应避免与 primary 指向同一物理位置。
  • 修改 backup name 会影响历史同步元数据的识别,生产环境应谨慎变更。

从源码看,backup 的 name 还有两项硬性约束:不能使用保留名 "primary",且整个列表中不允许重名(crates/ragfs/src/multibackend/factory.rs)。每个 backup item 支持 timeoutencryptionoperationsexcludes 等可选字段(crates/ragfs/src/core/types.rs)。

S3 兼容存储注意事项

使用 S3 兼容服务(MinIO、RustFS、Ceph 等)时,s3 段需要额外配置以下字段:

字段 是否必填 说明
use_path_style 大多数 S3 兼容服务必填 设置为 true 使用路径风格 URL(http://host/bucket/key)。大多数 S3 兼容服务需要此配置。
directory_marker_mode S3 兼容服务必填 必须显式设置为 "none"。如果不配置,RAGFS Rust binding 启动时会报 AGFSConfigError: invalid directory_marker_mode: null 并静默崩溃。
use_ssl 可选 HTTP 端点(如 http://localhost:9000)需要设置为 false

S3 兼容存储最小示例(RustFS/MinIO):

{
  "name": "s3-backup",
  "backend": "s3",
  "s3": {
    "bucket": "my-bucket",
    "endpoint": "http://localhost:9000",
    "access_key": "your-access-key",
    "secret_key": "your-secret-key",
    "prefix": "openviking",
    "use_ssl": false,
    "use_path_style": true,
    "directory_marker_mode": "none"
  }
}

为什么需要 directory_marker_mode

S3 兼容存储服务对"目录"的处理方式与 AWS S3 不同。RAGFS Rust binding 必须知道创建目录时是否需要写入目录标记对象。合法取值为 "none""empty""nonempty"。对于不使用目录标记的 S3 兼容服务(RustFS、MinIO、Ceph 等),设置为 "none"。如果省略,Rust binding 默认值为 null(不合法),导致服务端在启动时静默崩溃,报错 AGFSConfigError: invalid directory_marker_mode: null

这一行为在源码中有明确证据:S3FS 插件在 validate 阶段只接受 noneemptynonempty 三种取值,其它值直接返回 invalid directory_marker_mode 配置错误(crates/ragfs/src/plugins/s3fs/mod.rs);而 use_path_style 的默认值在客户端构造时是 truecrates/ragfs/src/plugins/s3fs/client.rs),directory_marker_mode 的插件级默认值是 "empty"crates/ragfs/src/plugins/s3fs/mod.rs)——因此在对接不使用目录标记的 S3 兼容服务时,显式写 "none" 是避免启动崩溃的关键。prefix 可用于同一 bucket 内的命名空间隔离(如 agfs/)。如果未显式提供 access/secret key,SDK 会走默认凭证链。

Docker 网络配置

在 Docker 中运行 OpenViking 并配置同主机的 S3 备份时,需要注意:

  • Linux Docker:使用 --network host 或宿主机局域网 IP。Docker bridge 网络可通过网关 IP(如 172.17.0.1:9000)访问宿主机局域网。
  • macOS/Windows Docker Desktop--network host 不支持。S3 端点使用 host.docker.internal(映射为宿主机的 localhost),或使用宿主机局域网 IP。

如果启用 S3 备份后服务静默崩溃,请优先排查 Docker 网络。RAGFS Rust binding 在容器内无法访问 S3 端点时会报 dispatch failure 错误。

同步模式选择

多写支持两种一致性模式,由 backups.sync_type 控制:

模式 配置值 行为 适用场景
异步多写 async primary 写成功后立即返回,backup 后台同步 低延迟写入、最终一致
同步多写 sync primary 写成功后等待 backup 确认 更强写入确认、可接受额外延迟

异步模式

异步模式适合大多数场景。

{
  "backups": {
    "sync_type": "async",
    "items": []
  }
}

特点:

  • primary 写入成功后立即返回。
  • backup 写入在后台执行。
  • 写入延迟低。
  • backup 可能短暂落后。

适合:

  • 写入吞吐优先。
  • backup 主要用于灾备。
  • 可以接受最终一致性。

从源码看,异步模式通过 fanout_async 以后台任务方式扇出写入,并使用 PathSerializer 按"路径 + backup 名"串行化同一路径的多次写入,防止 backup 端乱序应用(crates/ragfs/src/multibackend/meta.rs)。build 时若存在 write-enabled backup,会自动拉起后台 retry_loop 兜底修复(crates/ragfs/src/core/multibackend_wrapper.rs),重试间隔默认 30 秒、退避基数默认 1000ms、每轮每目标最多 3 次、连续失败 9 次后进入隔离(quarantine)(crates/ragfs/src/multibackend/factory.rs)。

同步模式

同步模式会等待 backup 确认。

{
  "backups": {
    "sync_type": "sync",
    "write_ack_count": 1,
    "write_ack_timeout_ms": 5000,
    "items": []
  }
}

参数说明:

参数 说明
write_ack_count 写入返回前至少需要多少个 backup 确认
write_ack_timeout_ms 等待 backup 确认的超时时间,单位毫秒

特点:

  • 写入确认更强。
  • 写入延迟受 backup 影响。
  • 未确认的 backup 会继续由后台重试修复。
  • primary 已写成功但 backup 未达确认数时,客户端可能收到错误;此时 primary 中可能已经存在数据。

适合:

  • 希望尽量减少 primary 与 backup 的确认窗口。
  • backup 延迟可控。
  • 调用方能接受同步写入带来的额外延迟。

源码中,sync_typesyncfanout_sync 会并行向所有目标写入并等待达到 write_ack_count 个确认(不足时返回 SyncWriteQuorum 错误),write_ack_timeout_ms 对应确认等待超时(crates/ragfs/src/multibackend/config.rscrates/ragfs/src/core/multibackend_wrapper.rs)。值得注意的是:同步模式并不代表"全有或全无",未达到确认数的 backup 会留在 .sync_log.json 中,由后台重试继续修复;因此调用方收到失败时 primary 数据可能已存在。

BackendsConfig 还提供以下可选的调优字段(crates/ragfs/src/core/types.rs):

参数 说明
write_concurrency 异步写入的并发上限(通过信号量控制)
retry_interval_ms 后台重试循环间隔(默认 30000)
retry_backoff_base_ms 重试退避基数(默认 1000)
retry_max_retries_per_round 每轮每文件/目标最大重试次数(默认 3)
retry_quarantine_after_failures 连续失败多少次后隔离该文件/目标对(默认 9)

配置读加速

backup 默认不参与读取。要让 backup 服务读取,需要显式配置 operations

{
  "name": "cache-backend",
  "backend": "memfs",
  "operations": [
    {
      "operation": "read",
      "priority": 10
    }
  ]
}

读取优先级规则:

  • priority 越小越优先。
  • 只有声明 read 的 backup 才参与读取。
  • primary 始终作为最终兜底。
  • 冷备 backup 不建议配置读能力。

如果一个 backup 只配置了 read,没有配置 write,它不会接收普通多写复制。只有在你明确知道该 backend 的数据来源时,才应使用这种配置。

源码中的读取路径与上述规则完全一致(crates/ragfs/src/core/multibackend_wrapper/routing.rs):

1. 按 priority 升序访问 read-enabled backup
2. 回退到 primary
3. 如果文件被 redirect,则访问 redirect target
4. 仍未命中则返回 NotFound

read_backups_sorted()priority 升序(缺失则视为最大值)排序(crates/ragfs/src/core/multibackend_wrapper.rs)。读路由还会记录 backup/primary/redirect/miss 命中等指标(read_route_metrics),可用于运维观测各后端读取命中情况。这种设计避免冷备节点默认参与读取,降低读到旧数据的风险。

Redirect 配置

Redirect 用于把匹配的文件写入指定 backup,而不是写入 primary。

按扩展名重定向:

{
  "storage": {
    "agfs": {
      "backend": "local",
      "redirects": [
        {
          "type": "FileExtensionPolicy",
          "extensions": ["(pdf|ppt|zip)"],
          "target": ["object-store"]
        }
      ],
      "backups": {
        "items": [
          {
            "name": "object-store",
            "backend": "s3",
            "s3": {
              "bucket": "openviking-large-files",
              "endpoint": "https://s3.example.com"
            }
          }
        ]
      }
    }
  }
}

按大小重定向:

{
  "type": "FileOverSizePolicy",
  "max_size_mb": 100,
  "target": ["object-store"]
}

注意:

  • target 必须引用已有 backup 的 name
  • redirect 文件仍会通过普通 API 呈现为可读、可列举、可查询状态。
  • redirect 映射保存在 primary 的内部元数据中。

从源码看,Redirect 策略配置在 primary 上(PluginConfig.primary_redirects),策略匹配发生在写入管线的最前端:execute_write_with_redirect 先评估 check_redirect(path, size),命中后直接把文件写入第一个可用 target,再把映射记录到 .redirect.json 内部元数据中(crates/ragfs/src/core/multibackend_wrapper.rs)。FileOverSizePolicymax_size_mb * 1024 * 1024 字节比较,FileExtensionPolicyextensions 是正则模式,会先按正则匹配文件名、失败时退化为后缀匹配(crates/ragfs/src/core/multibackend_wrapper.rs)。用户执行 ls()stat()read() 时仍能看到正常的文件系统视图,因为目录列表会把 redirect 条目合并回来。

常见用途:大文件进入对象存储、特定后缀文件进入专门 backend、主存储只保存常规内容。

Exclude 配置

Exclude 用于让某个 backup 跳过匹配文件。

{
  "name": "cache-backend",
  "backend": "memfs",
  "excludes": [
    {
      "type": "FileOverSizePolicy",
      "max_size_mb": 50
    },
    {
      "type": "FileExtensionPolicy",
      "extensions": ["(mp4|zip)"]
    }
  ]
}

常见用法:

  • 缓存 backend 排除大文件。
  • 低成本备份排除无需保存的文件类型。
  • 某个 backup 只保存文本或配置类资源。

如果 redirect 的目标 backup 同时 exclude 了该文件,说明配置互相冲突。请优先修正配置,不要依赖系统自动猜测其他目标。

源码层面的规则是:Exclude 策略配置在 backup 上,只影响该 backup 是否接收写入;write_targets() 在扇出前会过滤掉命中 exclude 的 backup(crates/ragfs/src/core/multibackend_wrapper.rs)。同时,配置校验器会拒绝在 exclude 策略中携带 target 字段,防止无意义的目标指向(crates/ragfs/src/multibackend/config.rs)。

加密配置

多写存储复用 OpenViking 的透明静态加密能力。

全局加密开启示例:

{
  "encryption": {
    "enabled": true,
    "provider": "local",
    "local": {
      "key_file": "~/.openviking/master.key"
    }
  },
  "storage": {
    "workspace": "./data",
    "agfs": {
      "backend": "local",
      "backups": {
        "items": [
          {
            "name": "plain-cache",
            "backend": "memfs",
            "encryption": {
              "enabled": false
            }
          },
          {
            "name": "encrypted-backup",
            "backend": "local",
            "local": {
                "workspace": "./data/encrypted-backup"
            },
            "encryption": {
              "enabled": true
            }
          }
        ]
      }
    }
  }
}

规则:

  • 全局 encryption.enabled=true 时,primary 必须加密。
  • backup 可以通过 encryption.enabled 单独控制是否加密。
  • Python SDK、HTTP API 和 CLI 不需要处理加解密。
  • .redirect.json.sync_log.json 等内部元数据会跟随 primary 加密策略。

源码中,validate_primary_encryption_flags 会在全局加密开启时强制 primary 的 server_encryption_enabledprimary_encryption_enabled 均为 true,否则直接报配置错误(crates/ragfs/src/multibackend/config.rs);factory.rs 组装时,backup 的加密状态默认继承全局设置(除非 item 显式设置 encryption.enabled=false),并只对支持 replace() 语义的 localfss3fsmemfs 三类后端做加密包装(crates/ragfs/src/multibackend/factory.rscrates/ragfs/src/multibackend/factory.rs)。内部元数据通过 MetaStateStore 全部经由 primary backend(即加密入口)读写,天然继承 primary 的加密策略(crates/ragfs/src/multibackend/meta.rs)。

存量数据迁移

多写只复制启用之后的新写入,不会自动复制历史文件。

推荐迁移流程:

  1. 停止或冻结写入窗口。
  2. 使用 OVPack 或其他受控工具把存量数据迁移到目标 backup。
  3. 校验目标 backend 的数据完整性。
  4. 配置并启用 storage.agfs.backups
  5. 恢复写入。
  6. 观察同步状态和错误日志。

如果无法冻结写入,可以先做一次全量迁移,再短暂停写做增量校验,最后启用多写。

多写与 OVPack 导入导出 的分工是:OVPack 负责历史数据迁移,多写负责启用后的持续复制。若需在启用多写前先了解存量数据规模与存储整体架构,可参考 存储架构

验证配置

启动前建议运行:

openviking-server doctor

doctor 是 OpenViking CLI 的原生子命令,用于校验子系统并输出可操作诊断,会检查配置中的未知或非法字段(openviking_cli/doctor.py)。

启动后可以用普通文件 API 验证:

openviking write viking://resources/multiwrite-check.txt \
  --content "multi-write check" \
  --wait

openviking read viking://resources/multiwrite-check.txt

viking:// URI 是 OpenViking 的统一资源定位格式(viking://<scope>/<path>resources 为独立资源作用域),write/read 等命令直接复用普通文件 API(openviking_cli/utils/uri.py)。

如果使用本地 backup,可以直接检查 backup 目录中是否出现对应文件。生产环境更推荐使用系统健康检查和同步状态命令。除此之外,还可通过读路由指标(read_route_metrics 中的 backup/primary/redirect 命中计数)从运维侧确认备份确实在参与读写(crates/ragfs/src/core/multibackend_wrapper/routing.rs)。

常见问题

为什么 backup 没有参与读取?

backup 默认只参与写入,不参与读取。需要在 backup 上显式配置:

{
  "operations": [
    {
      "operation": "read",
      "priority": 10
    }
  ]
}

为什么启用多写后历史文件没有出现在 backup?

多写只处理启用后的新写入。历史文件需要先通过 OVPack、对象存储复制或后续 backfill 能力迁移。

异步模式下能否保证立即读到 backup 的最新数据?

不能。异步模式只保证最终一致。需要强读一致时,应让读取回退到 primary,或避免让可能滞后的 backup 参与读路由。

内部元数据文件会出现在用户列表里吗?

不会。.redirect.json.sync_log.json 是内部文件,会被普通目录列表隐藏。源码中 is_hidden_internal_name 将这两个文件名(连同 .path.ovlock.exact.ovlock.* 锁文件)统一判定为隐藏内部名(crates/ragfs/src/core/internal_names.rs),它们也不应通过公开 API 直接读写。

sync 模式返回失败是否表示 primary 一定没写入?

不是。primary 写成功但 backup 未达到确认数时,客户端可能收到失败。此时 primary 数据可能已经存在,落后的 backup 会由后台重试修复。

已知限制

  • 异步模式下 backup 可能短暂落后。
  • 启用多写前的历史文件需要单独迁移或回填。
  • redirect 文件依赖内部元数据恢复目录视图。
  • 多进程同时写同一 primary 时,需要未来的分布式元数据锁能力。
  • 热点目录会频繁更新内部元数据,可能带来额外写放大。

相关文档

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395