CPU / TCP 开发中

从 1 个节点扩展到 3 个,再到 10 个

从一个分片开始,增加成员时沿用相同的客户端 API。当前 CPU/TCP 路径需要运维协调:启动一个新服务进程不会自动入群,应用必须向每个 ShardedClient 发布完整的路由快照。

可运行示例在同一个进程内启动 1、3、10 个 localhost TCP 服务,验证软件契约。它不等于 10 台物理机验证,也不证明生产可用性、GPU 容量或吞吐。RDMA、GDS、BlueField-3 和真实引擎集成仍是 NOT_RUN_HARDWARE。

先确定拓扑

阶段固定分片 ID地址示例epoch 要求
1 个节点010.0.0.11:9735初始实际 epoch E
3 个节点0–210.0.0.11–13:9735每个节点均大于上一轮最大 epoch
10 个节点0–910.0.0.11–20:9735每个节点均大于上一轮最大 epoch

表中的私网地址仅作示例,请替换为实际可达地址。每个服务使用独立数据目录,端点对应唯一且稳定的分片 ID。ID 配置在客户端路由中,服务端没有 --shard-id 参数,也没有 --join、种子发现、仲裁或副本数配置。完整 CacheIdentity 通过 rendezvous hash 选择一个归属分片,不会把同一份数据写入所有节点。

先运行本机验证示例

按快速开始准备环境后,从下方链接的源码版本运行。示例创建独立临时目录,在每次扩容时停止并等待旧服务退出,提升所有节点的 epoch,替换完整路由,验证旧 session 被拒绝,并对每个分片执行 Lookup / Put / Commit / Get / Drained 释放。结束后清理临时目录。

cargo run -p gpucache-transport --example scaling_smoke
cargo test -p gpucache-transport --example scaling_smoke

以下为成功执行的部分真实输出。完整输出还逐一验证三个阶段的分片 ID:0、0–2、0–9。这是正确性验证,不是性能测量。

status=SCALING_SMOKE_OK
topology=SINGLE_PROCESS_LOCALHOST
stage_shards=1 store_epoch=1 local_revision=1
stage_shards=3 store_epoch=2 local_revision=2
stage_shards=10 store_epoch=3 local_revision=3
per_shard_roundtrips=14
stale_sessions=REJECTED
automatic_payload_migration=NONE
durability_replication=NONE
hardware=NOT_RUN_HARDWARE
performance=NOT_A_PERFORMANCE_RESULT

启动 1 个节点

以下命令启动一个独立的 FileStore 服务,仅监听本机。示例 fingerprint 是公开的开发测试值,非本机测试请配置自己的凭据与租户策略。在应用中创建 ShardedClient:配置分片 ID 0、此地址和 TCP 握手返回的实际 epoch;使用其 lease/session API,保证 Put 和 Commit 仍指向选定的归属节点。

cargo run -p gpucache-transport --bin gpucache-tcp -- serve \
  --data-dir ./data/shard-0 --bind 127.0.0.1:9735 \
  --tenant 1 --trust 1 \
  --fingerprint 0000000000000000000000000000000000000000000000000000000000000001

跨机器时,只在隔离可信网络上绑定节点私网地址,例如 --bind 10.0.0.11:9735 --allow-remote。--allow-remote 不会启用 TLS 或加密的对端身份认证,fingerprint 是通过明文 TCP 传输的持有者凭据。CLI 负责启动服务,分片客户端仍需由应用代码构建。

在第一阶段之前配置客户端

在已有认证上下文和经核实的初始端点的应用代码中,创建客户端时就按最终拓扑设置上限。replace_routes 不能修改 ClientLimits。以下配置在三个阶段都允许最多十个分片、每分片一个连接:

use gpucache_transport::{ClientLimits, ShardedClient};

let limits = ClientLimits {
    max_shards: 10,
    max_connections: 10,
    max_connections_per_shard: 1,
    max_frame_bytes: 2 * 1024 * 1024,
    ..ClientLimits::default()
};
let client = ShardedClient::new(auth, endpoints, limits)?;

默认的每分片连接上限是 4;如果仅把 max_connections 改为 3 而保持该默认值,配置会被拒绝。每个服务默认最多接受 16 个连接,由所有客户端共享;每个客户端进程有独立连接池,需规划总连接数。

CLI 服务端的帧正文上限是 2 MiB,上面的 max_frame_bytes 与其一致。协议不会协商帧上限,客户端与服务端需要配置一致。独立的 scaling_smoke 示例则在两端都使用匹配的 1 MiB 库默认值。

新客户端的本地 revision 从 1 开始,与实际 store epoch 无关。完成协调的服务切换后,读取该客户端当前 revision,再调用 client.replace_routes(revision, endpoints) 发布完整快照。revision 属于各客户端,不是 store epoch,也不是共享的共识任期。

从 1 个节点扩到 3 个

  1. 准备新分片 ID 1、2:独立存储目录、一致的租户与 trust 策略、可达的地址。沿用第一阶段之前选定的客户端上限,并检查服务端、pin、字节和重试配额。
  2. 暂停所有能访问旧归属节点的客户端,停止新的 Lookup/Get/Put/Commit。等待传输完成,以确认完成的 Drained 释放 pin;租约过期本身不代表传输已排空。完成或取消、隔离未完成的写入,不跨轮次复用旧租约。
  3. 停止旧服务并等待所有 worker 退出,或执行可验证的外部隔离,阻止旧节点继续读写。replace_routes 只改变本地客户端状态,不能隔离远端服务。不能证明旧节点已隔离,就不要发布新路由。
  4. 独占每个数据目录,准备好存储,使下一次服务握手的 epoch 严格大于上一轮快照的最大值。重启旧节点;新目录按需初始化并重新打开。对外恢复访问前,检查每个节点实际握手结果。
  5. 构建包含 ID 0、1、2 全部实际地址和 epoch 的完整快照。对每个暂停的客户端读取 let revision = client.stats().revision;,然后调用 client.replace_routes(revision, endpoints)。必须处理拒绝结果,不能只发布新增节点;确认所有必要客户端接受同一完整快照后再恢复流量。
  6. 切换完成后,为新的逻辑请求获取新 session。在同一有效 epoch 内,一个 generation 后续的 Commit/Get/Release 必须保持原分片和 epoch,不能重新路由这些仅带 generation 的后续操作。已被隔离的工作应丢弃,不能改写 epoch 重试。重新映射的 identity 不会自动迁移:未命中或出错后重算并显式回填。可选冷层恢复仍在热路径之外。

epoch 易错点:新 FileStore 首次打开为 1,同一已有目录每次重新打开增加 1。当前没有 --epoch 参数或管理员 epoch RPC。上一轮最大值为 1 时,三个端点都必须至少为 2,包括两个新节点;上一轮最大值为 7 时,全部至少为 8。示例通过独占目录、有限次重新打开来准备 epoch,实际部署需提供等价编排。不要手工修改 MANIFEST。

从 3 个节点扩到 10 个

对 ID 0–9 重复「暂停 → 排空 → 停止或隔离 → 准备 epoch → 发布完整快照 → 验证 → 恢复」。演示从全 1 到全 2 后,下一轮可用 3;实际部署必须依据观测到的上一轮最大值,不能按节点数推算。使用创建客户端时已规划的上限;替换路由不会调整这些上限。逐分片规划租户配额、重试表、pin/region、帧大小、内存和磁盘容量;增加节点不会取消这些限制。

客户端直接访问选中的归属节点;ShardDirectory 只保存有界元数据,不集中保存所有数据。一个节点丢失后,其键会未命中,不会转去热副本。三个节点不代表存在仲裁,十个节点也不证明支持一万张 GPU。容量和键分布需要用实际负载验证,本手册不承诺吞吐线性增长。

验收、观测与回滚

  • 记录源码提交、每个固定分片 ID、地址、实际 epoch,以及每个客户端采用的完整快照版本。检查 ID 和端点均无重复。
  • 为每个分片选择归属该分片的键,执行 Lookup → Put → Commit → Get、字节比对和 Drained 释放;验证跨租户或错误 identity 不会命中。generation 仅在分片内有效,必须同时保留分片 ID 与 epoch。
  • 成员变化后验证旧 session/lease 被拒绝,旧归属节点上的未完成写入不能跨轮次提交。测试停止一个节点,确认超时或未命中触发重算,绝不返回别的 identity 的数据。
  • 查看客户端统计与存储 drain、quarantine、fence 报告;分别统计逻辑描述符字节和线传字节,以及配额失败、重试、连接使用情况。运行仓库 CPU 回归;该结果不代替真实多机验收。
  • 发布或冒烟检查失败时保持流量暂停。修复快照,或再次执行协调停止、隔离,为每个端点使用更高 epoch 后回到旧节点集合。禁止回退旧 epoch/旧快照或恢复旧 lease;丢失的热数据可以重算。
python3 scripts/ci/vm_regression.py --profile cpu --offline \
  --output artifacts/scaling-cpu

对应源码

以仓库操作手册和可执行示例为 API 依据。当前没有开箱即用的集群管理器、在线重分片或数据迁移,也没有滚动升级可用性保证或生产多机验证结论。

SCALING.md · scaling_smoke.rs · OPERATIONS.md · SECURITY.md