网络文件系统(network://)使用指南——RemoteFileSystem 桥
给谁看:需要"本地不落地 / 容量无界 / 共享"文件 IO 的组件开发者(引擎、数据结构、备份归档)。 回答什么:网络文件系统怎么构造、staging 写回层怎么编排、Flush 唯一持久化点契约、增量与恢复协议。 本篇只讲机制——S3 协议特有部分(SigV4/厂商矩阵/COS vhost/chunked 链签等)见
../TC.Tier.Core.IO.S3/docs/s3-protocol.md。定位:网络文件系统是 Core 层四类文件系统之一(本地/内存/虚拟/网络), 与
RemoteFileSystem桥 +IObjectStore注入点共同构成——任何IObjectStore实现 升格为统一文件 IO 协议(与本地/内存文件系统平权)。S3 是IObjectStore的协议实现之一 (见../TC.Tier.Core.IO.S3/docs/s3-protocol.md); 其他协议实现按相同契约接入即可。
0. 一句话总纲
RemoteFileSystem 把任意 IObjectStore 升格为 IFileSystem 统一协议下的网络文件系统(四类之一)——
消费者只认 IFileSystem/IFileHandle,从不问"数据在哪"。
network:///s3/... 中的 s3 是协议头(与 IO.S3 程序集自动注册绑定);换协议头即换对象层实现,
桥层零修改。
1. 构造与打开
using TC.Tier.Core.IO;
using TC.Tier.Core.IO.Remote;
// 1. 选 IObjectStore 实现(S3 / 其他协议适配)
using var store = S3ObjectStore.Create(cosOptions); // S3 协议见 s3-protocol.md §1
// 2. 桥升格为 IFileSystem(与本地/内存文件系统同构)
using var fs = RemoteFileSystem.OpenOrCreate(store, new RemoteFileSystemOptions
{
KeyPrefix = "engine-a/", // 多引擎共桶隔离(对象键 = KeyPrefix + path)
Spill = RemoteSpill.ToDisk("/var/tmp"), // staging 超内存预算的落盘根(null=纯内存超限 DiskFull;无盘形态用 RemoteSpill.ToMemory())
});
// 3. 打开语义与本地/内存文件系统完全同构(IFileSystem 契约)
using var w = fs.Open("seg-001", new FileOpenOptions
{
Access = AccessMode.ReadWrite,
Mode = FileOpenMode.OpenOrCreate, // ★ 显式给——裸默认是 OpenExisting(与其他文件系统同规则)
});
spec 形态(TierFs.Open("network:///s3/...") 工厂自动构造桥——见 io.md §2.1):
network:///s3/host[:port]/bucket/prefix ← 协议首段必填(s3 随 IO.S3 程序集自动注册)
?label=.."a=100G&access=ro|wo|rw&exclusive=1&cred=env:NAME&spill=local:///var/tmp
2. RemoteFileSystemOptions 调参表
| 参数 | 默认 | 说明 |
|---|---|---|
KeyPrefix |
"" |
命名空间隔离;path 经共享校验规则(分隔符/越根/非法字符全拒)——不可能访问同桶其他前缀 |
StagingMemoryLimit / StagingPageSize |
64MB / 64KiB | 写句柄 staging 预算与页粒度(延迟加载/回填的最小单位) |
Spill |
null | 超预算落点(RemoteSpill.ToDisk(dir) 磁盘目录 / RemoteSpill.ToMemory() 内存私有卷——互斥二态;null = 超限 DiskFull) |
MultipartThreshold / PartSize / MaxParts / MaxConcurrency |
8MB / 8MB / 10000 / 4 | Flush 的 multipart 编排 |
ReadCacheBytes / PrefetchPages |
4MB / 4 | 读句柄页缓存与预取窗口(Advise(Sequential) 放大 4×) |
LeaseTimeout |
60s | fencing 卷锁租约(心跳超时接管窗口) |
OrphanUploadCleanup |
null | 非空 = 构造时扫描清理早于阈值的残留 multipart 会话(崩溃碎片回收) |
Preallocation |
Metadata |
预分配方式轴(IS-04 统一词汇):network 无预分配概念(对象存储无块布局——厂商配额);显式请求 Full 在挂载入口抛 IOError.Unsupported(能力位诚实不置 + 不静默) |
3. 核心语义(与本地/内存文件系统的差异——必须知道的三条)
3.1 ★ Flush 是唯一持久化点
Write/Append 只进 staging(页缓存的远端同构物)即返回;
任何 Dispose 都不触发上传——池内句柄 Dispose = 归还(staging 留池续用)、池外 Dispose = 关闭
(未 Flush 的数据丢弃 = "未 fsync 即丢")。需要"用完即持久"必须显式:
using var h = pool.Acquire("seg-001", opts); // 或 fs.Open
h.Append(payload);
h.Flush(); // ← 唯一持久化点(multipart complete——此前崩溃旧对象完好)
Dispose 方向同本地文件系统("离开目录"——仅释放桥自持资源,已开句柄继续有效);与
内存/虚拟文件系统的"拔盘/关卷"语义不同(见 io.md §置顶二)。
3.2 读句柄不追新
读句柄的 Length 和数据是 Open 时刻的快照——其他句柄 Flush 的追加对它不可见, 需要追新 = 重新 Open。写句柄自身的 read-your-writes 天然成立(读走 staging)。
3.3 随机覆写已有大文件 = 逐区间按需拉取(延迟加载)
纯追加句柄永不拉历史(追加路径零网络);
随机覆写会按页粒度 Range GET 拉满所触区间——GB 级文件秒级 + 全额带宽。
追加式访问模式是网络文件系统的正确姿势(RandomWrite 能力位不置位即为此)。
常用操作速查:Append 原子预留(同 fs 跨句柄落点不交)/ PunchHole 读零语义(无对齐约束——
AllocationUnit=1)/ SetLength 截断后扩展读零(不复活旧数据)/ CollapseRange·InsertRange 抛
Unsupported(能力位表达;Lock = 进程内 advisory、Map = 物化映射,均可用)/ Move = 服务端 Copy+Delete /
EnumerateEntries = 前缀枚举(恢复扫描零额外 Head)/ xattr = PUT 原子快照(Flush 随对象提交)。
4. 增量 Flush 与恢复
- 增量 Flush:二次 Flush 只上传脏 part,未改 part 走服务端自拷贝(零出口流量)——追加负载的 出口流量 O(增量) 而非 O(总长)。
- 洞元数据读加速:
PunchHole后 Flush 会把洞区间编码进对象元数据——后续读句柄命中洞区间本地 返零、不发 GET。 - 恢复:
fs.EnumerateEntries()返回融合列表(FsEntry:Name/Length)——引擎扫段重建段表与本地文件系统路径同构; 崩溃未 Flush 的 staging 丢 = 未 fsync 丢,恢复协议零修改。 - fencing 卷锁:
fs.AcquireExclusive(timeout)= lock 对象 + 条件 PUT 抢建 + 心跳超时接管 + token 防误删——尽力型(仅防意外双开;引擎正确性由段表 lease 单写者协议承担)。
5. 能力位矩阵(与本地/内存/虚拟文件系统的差异)
| 能力 | 本地 | 内存 | 虚拟 | 网络 |
|---|---|---|---|---|
| DIO(无缓冲) | ✓(探测扇区) | ✗(不置位) | ✓(按载体) | ✗(不置位) |
| 稀疏 / PunchHole | ✓(按对齐) | ✓(字节粒度) | ✓(块粒度) | ✓(字节粒度——AllocationUnit=1) |
| 随机覆写 | ✓ | ✓ | ✓ | 延迟加载(按页 Range GET) |
| 内存映射 | ✓ | ✓(视图写回) | ✓ | ✓(物化映射——Read=Range GET 快照/写回 staging) |
| 范围锁 | ✓(advisory) | ✓(进程内真锁) | ✓(advisory) | ✓(进程内 advisory) |
| Append 原子预留 | ✓(fs 级) | ✓ | ✓ | ✓(staging 内即返回) |
| 持久化点 | h.Flush()(fsync) |
导出镜像(§5.2) | h.Flush()(superblock 轮写) |
h.Flush()(PUT/multipart complete) |
完整四类文件系统平权矩阵见 medium-parity-matrix.md。
6. 想深入?指路
| 想懂什么 | 去哪 |
|---|---|
| S3 协议特有部分(SigV4 签名/厂商矩阵/凭证源/重试/chunked 直传/COS vhost 适配) | ../TC.Tier.Core.IO.S3/docs/s3-protocol.md |
| Core/IO 总入口(四类文件系统选型/spec/能力协商/陷阱清单) | io.md |
| 虚拟文件系统(TierVolume 深指南) | virtual-file-system.md |
| 采集/还原/迁移管线(TCA1/快道/启动四形态) | IO/Image/RootSpaceImage.cs 注释与 io.md §5.2 |
| 桥级契约测试(staging 语义/池协议/增量断言/洞读零 GET/孤儿清理) | tests/TC.Tier.Core.Tests/IO/Remote/ |
7. 常见陷阱(网络文件系统专属——通用陷阱见 io.md §7)
| # | 陷阱 | 要点 |
|---|---|---|
| N1 | 池归还 ≠ 上传 | 池内 Dispose = 归还(staging 留池续用);池外未 Flush Dispose = 丢弃 |
| N2 | RemoveAll 不得 flush |
删段后 flush 会复活已删对象——三出口(Release(close:true)/RemoveAll/pool.Dispose)全部不 flush |
| N3 | 读句柄是快照 | 他人 Flush 的追加对读句柄不可见;追新必须重新 Open |
| N4 | 随机覆写代价 | GB 级文件秒级 + 全额带宽——追加式访问是正确姿势 |
| N5 | fencing 是尽力型 | 仅防意外双开;强一致由段表 lease 协议承担 |
| N6 | vhost × COS | COS 必须 UseVirtualHostAddressing=true;path-style 下整段路径被当 key |