摘要

  • RFC 3010 把稳定的客户名称与具体运行实例分开:会随重启改变的 verifier、协商所得的 clientid 和带认证的确认流程,阻止同名新实例悄悄继承旧锁。
  • 服务器丢失状态后,一个通常等于租期的宽限窗口先接纳 reclaim,并暂时拒绝可能冲突的新锁与受保护 I/O,使连续性成为有限恢复程序,而不是永久权利。

文件服务器重启时,磁盘上的文件可以毫发无损,锁表却可能一片空白。客户仍相信自己拥有排他访问,服务器却已经没有那段记忆。若它立刻把同一范围交给新客户,旧承诺就会被重启偷偷取消;若它永远等待,已经消失的客户又能让系统停摆。

RFC 3010 在 2000 年 12 月给出的第一版 NFSv4 完整协议,把这段矛盾时间变成了可执行的恢复次序。NFSv4 把锁、打开状态、安全协商和缓存委托纳入协议。当服务器开始为跨调用权利保存状态,故障恢复就必须拥有自己的证据和时钟。

第一步不是辨认机器,而是辨认机器的这一次运行。客户提交一个可以长期稳定的 opaque id,同时提交每次初始化后应改变的 verifier。主机名可以回答“是哪台设备”,却不能回答“是哪次启动”。后者才决定内存里的旧锁是否仍属于当前实例。

SETCLIENTID 用一次协商把名称、verifier 与 RPC 请求中的认证主体连接起来。服务器返回较短的 64 位 clientid 和确认用 verifier;客户再用 SETCLIENTID_CONFIRM 完成闭环。之后 clientid 可以高效引用状态,但它只是这台服务器发出的本地简称,不是全球身份、所有权证书或持有即有效的授权令牌。

相同名称甚至可能意味着冲突。错误复制的高可用配置、重复地址或恶意客户都可能产生同一个 opaque id。RFC 3010 允许服务器返回 NFS4ERR_CLID_INUSE,而不是把两个实例的权力合并。同名只能触发核查,不能证明二者是一位主体。

若同一认证主体带着新 verifier 回来,服务器可以判断客户已经进入新的运行纪元,原来依赖易失内存的锁状态也已丢失,于是释放旧 clientid 下的锁。但这项动作必须受认证约束。陌生人不能仅凭编造一个新 verifier,便让别人的锁消失。verifier 证明的是实例变化,RPC principal 规定谁有资格声明这项变化。

第二条边界是租期。服务器规定统一的 lease interval;建立或使用状态的正常操作可以隐式续租,闲置客户也可发送 RENEW。一次续租覆盖客户的全部相关状态,因此锁数量不会线性放大心跳流量。

租期不是永久占有。客户超过窗口没有续约,服务器便可以回收状态并把冲突权利授予他人。长时间网络分区后,旧 stateid 可能收到 NFS4ERR_EXPIRED。客户此时必须把排他性已经丢失的事实传给应用,而不能凭自己的旧记忆继续假定服务器仍受约束。

服务器重启产生相反的不对称:客户还记得 stateid,服务器却不认识它。NFS4ERR_STALE_STATEID 与 NFS4ERR_STALE_CLIENTID 是服务器纪元改变的收据。它们要求客户先取得新 clientid,再进入状态恢复,不能把普通请求直接塞进已经作废的命名空间。

核心机制是 grace period。它通常持续一个租期。在这段时间,旧客户可以用 reclaim 形式的 LOCK、OPEN 和 CLAIM_PREVIOUS 重建状态;最简单而安全的服务器做法,则是用 NFS4ERR_GRACE 拒绝新的锁、打开、读取与写入。服务进程已经恢复,但一部分授予权被刻意暂时关闭。

这项拒绝保护的是尚未出现的人。原锁持有人可能还在检测故障、重新连接或整理恢复清单。如果先到的新请求直接获得冲突状态,故障后的网络速度就会被误当成所有权。宽限期让旧主张获得一次有限、可截止的回归机会。

暂停并非机械冻结。若服务器保存的证据足以保证未来任何 reclaim 都不会与当前操作冲突,也不会因当前授予而被拒绝,它可以在宽限期内处理新工作。稳定存储中的锁记录或计数可以缩小不确定范围。优化的合法性来自证明,而非来自“服务器已经上线”的表象。

窗口结束后,历史也不能无限追赶现实。迟到的 reclaim 只有在服务器能保证重启后没有授予冲突锁或 I/O 时才可能成功。一旦新事实已经形成,未被保留的旧主张便不能无代价地覆盖它。时间限制把最坏的不确定性变成有界成本。

RFC 2624 先解释了 NFSv4 面对的设计压力;RFC 3010 给出第一套完整线协议。RFC 3530 后来取代它,RFC 7530 又成为之后的 NFSv4.0 规范。因此本文讨论的是机制史,不是今天的部署手册。

NFSv4.1 继续把收据做细。RFC 5661 引入 EXCHANGE_ID、session 与更完整的恢复模型;取代它的 RFC 8881 明确展示了一项交换:服务器越想在复杂故障后宽容地承认 reclaim,就越要把客户与状态证据可靠地保存下来。宽容不是免费政策,而是证据保管能力。

从 Lu Heng 的 Running-Code Primacy 视角看,真正有效的并非客户对旧锁的回忆,而是重启后的服务器能本地验证的组合:认证主体、当前实例、恢复模式、尚可重建的旧状态、宽限窗口以及没有被新冲突覆盖的事实。最小共同规则不是永久记住每项权利,而是不让新客户仅因故障后先到一步就夺走它。

稳定性谬误则出现在同一个名称、路径或服务器地址被当成连续权威时。RFC 3010 承认状态可能消失,用 stale 错误公开断裂,再用有限拒绝为恢复争取秩序。连续性不是字符串看起来没变,而是跨越不同运行纪元后仍能重建完整的证据链。

那把锁不会因为客户记得它就自动活下来。客户之所以获得恢复机会,是因为协议记住了不确定性应按什么顺序解决。这比“永不丢失”的承诺更小,却更可靠。

Sources