摘要

  • 对设置了新属性的目录,遵守规则的客户端不能用另一轮枚举所得的 READDIR 结果满足本轮读取,也不能为本轮返回的条目报告早于最近一次 READDIR 的属性值。
  • 这不是“所有缓存归零”。规则只约束某个目录路径、本轮实际返回的条目和应用可见答案;持有写委托的客户端若掌握比服务器更新的值,也不必倒退。
  • 它能降低备份与同步程序因旧尺寸、旧时间而漏掉新数据的风险,但会增加元数据负载,而且协议没有给服务器一张“客户端确已遵守”的收据。

同一个目录里有两只钟

目录的名字列表可以是对的,名字旁边的文件属性却已经过时。原因并不神秘:目录条目记录名字和 fileid,创建、删除或重命名会推动父目录的 change 属性;另一台客户端继续写一个已有文件,只会改变该文件的 size、time_modify 等属性,不必改变父目录的名字集合。

NFS 客户端可以把 READDIR 随条目返回的文件属性留在本地,让后续 stat 不再为每个文件发送 GETATTR。在稳定目录里,这是合理的效率交换。在高性能计算输出目录或高速落盘区里,成百上千个写入者会让任何正数缓存寿命都可能显得太长。名字快照仍有效,尺寸却属于上一个时刻。

这会改变业务结果。增量备份或目录树同步若依赖尺寸和修改时间决定是否复制,可能把已增长的文件判成未变化,静默漏掉数据。fattr4_uncacheable_dirent_metadata 让服务器指出哪些目录值得为每轮枚举重新取一次服务器观测。它没有给未设置属性的目录颁发“缓存可靠”证书。

第 11 版终于写出了禁止哪种答案

draft-ietf-nfsv4-uncacheable-directories-11 于 2026 年 9 月 5 日提交,是 NFSv4 工作组的活跃标准轨草案。文档报告了 Hammerspace 服务器与 Linux 客户端原型,但仍只是 Internet-Draft,不是 RFC、IETF 批准或生产部署证明。

第 10 版要求客户端“每次 READDIR 都从服务器获取元数据”。READDIR 本来就是发往服务器的协议操作,这句话没有封住真正漏洞:客户端可以重新拿到名字,却仍从普通 inode 属性缓存里把旧尺寸交给应用。

第 11 版改为规定外部行为。若目录设置了该属性,遵守规则的客户端不得用另一轮枚举的 READDIR 结果满足本轮 readdir。对最近一次 READDIR 返回的某个条目,它不得报告在那次 READDIR 之前已经收到的属性值,无论旧值原先由 READDIR、GETATTR 还是别的操作填入。

因此,合规测试不必检查内核怎样排布缓存。第一次枚举后,让另一台客户端写大文件,再做第二次枚举和 stat。被禁止的是早于第二轮 READDIR 的旧值。实现可以删除、覆盖、分代保存甚至继续持有旧记录,只要不拿它回答这次问题。

一轮枚举不是每个名字一次网络请求

新版本专门定义了容易混淆的单位。小写 readdir 是应用发起的目录读取;大写 READDIR 是 NFSv4.2 线上操作;enumeration 是对目录的一轮遍历,可能包含许多应用调用和若干续传 READDIR。一次 READDIR 通常能供给多个 readdir。

所以草案没有要求每返回一个名字就访问一次网络。它要求本轮遍历由本轮 READDIR 奠基。客户端应在 attr_request 中请求打算报告的属性;也允许事后逐项 GETATTR,只是那会重新制造每个条目一次请求的流量。

本轮取得的元数据在遍历结束后仍可保留。两轮之间,RFC 8881 的普通属性缓存寿命继续生效。下一轮枚举到来时,早于本轮 READDIR 的值才对本轮返回条目失去资格。所谓“不可缓存”实际是一条报告时限,不是对内存结构的禁令。

开关生效也不是一个瞬间

服务器把属性从 FALSE 改成 TRUE 时,客户端可能仍缓存着目录属性。草案允许它在尚未观察到变化时继续原行为。延迟由两种常规机制收敛:目录属性缓存达到上限后过期,或客户端重新验证时看到目录 change 已移动。

运维记录必须把三个时点分开:服务器写入政策的时间、每类客户端第一次看到新值的时间、应用第一次得到符合新规则答案的时间。只记录第一个时点,会把尚未发生的执行当成既成事实。

目录委托更直接地冲突:它让客户端不回服务器即可提供目录状态,而新属性要求每轮枚举回到服务器。因此属性为真时,服务器必须收回已存在的目录委托,也不得新发委托。子属性变更通知不是普遍替代品;它是可选能力,很多环境不实现,而且成本会随客户端数与变更数相乘。

服务器也可能不是最新权威

若客户端持有某文件的 OPEN_DELEGATE_WRITE,最新的 size 或 change 可能在客户端手里,服务器本地副本反而落后。RFC 8881 允许服务器通过 CB_GETATTR 向受委托客户端取回这些值。强迫它拿旧的服务器副本覆盖新值,会把正确答案改错。

第 11 版为此写出例外:规则针对“比服务器将返回的值更旧”的数据,不要求写委托持有者倒退。可见,“走了一次服务器”不是新鲜度定义;当前写权威属于谁,同样决定答案顺序。

即使没有委托,READDIR 返回的也只是服务器在那个观察时点能陈述的状态。下一次写入可以马上发生。新属性缩短并明确了旧观测被错误沿用的边界,没有创造文件数据与元数据的全局原子快照。

属性管的是一条路径,不是整个文件

属性设置在目录上。一个文件若通过硬链接同时出现在已标记目录和未标记目录,从后者访问不受前者约束。草案也不规定对子目录自动继承;若服务器选择继承,那是本地策略,不能从父目录的一个值推断整棵树。

第 11 版还修正了对象类型处理。属性支持能力按文件系统公布,因此支持它的服务器对非目录对象收到 GETATTR 时要返回 FALSE,而不是把属性假装成“不支持”;对非目录执行 SETATTR 则返回 NFS4ERR_WRONG_TYPE。文件系统认识某属性,不等于每个对象都适合设置它。

会查询,不等于会执行

该属性是建议性的。客户端查询 GETATTR 或发送 SETATTR,只证明它知道字段编号,不能证明它实施了答案边界。服务器无法仅凭协议动作区分“遵守”“理解但忽略”和“完全不支持”三类客户端,因此不应让自身正确性依赖所有客户端一致服从。

成本同样真实。标记目录的每轮遍历都会产生服务器工作;若客户端只取名字再逐项 GETATTR,负担更重。若普通用户能在热点目录任意切换属性,一次元数据修改就可能迫使所有遵守规则的客户端放弃复用,形成负载放大。设置权限、导出策略与容量上限必须和正确性目标一起设计。

公开材料能证明草案文本、版本历史、基础 RFC、公开提交说明与贡献者所称原型,不能证明 Linux 已发布版本、Hammerspace 产品行为、采用率、基准、事故或实际收益。另一份“不可缓存文件数据”草案处理文件内容与写持久性,不扩大本篇的目录元数据范围。

按 Heng Lu 的现实层原则,服务器属性是声明,客户端缓存是私有运行状态,READDIR 是服务器观测,stat 才是应用收到的陈述。第 11 版最有价值的变化,是允许这四层各自保持准确含义。

来源