Table of Contents

网络文件系统(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=..&quota=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() 返回融合列表(FsEntryName/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