摘要

  • RFC 9590 明确允许服务器在无法查询某个邮箱的注释时省略对应 METADATA 响应,而 LIST 仍以带标签的 OK 结束。
  • 显式 NIL、查询后无响应、\NonExistent 层级名称,以及仅因后代匹配而出现的父名称,是四种不同证据状态,不能一律写成“空”。

设想一个客户端要为顶层邮箱取得颜色注释。服务器返回邮箱名称、若干颜色,最后给出 A01 OK List completed。任务面板于是变绿。可有一个名称旁边没有颜色。

这并不足以说明“颜色未设置”。服务器可能明确返回了 NIL,也可能根本未能完成注释查询;该名称可能只是一个不存在的层级容器,也可能只是因为某个后代符合递归选择条件才被带入结果。命令成功只能回答命令是否结束,不能替这些局部事实作证。

RFC 9590 合并了往返,没有合并语义

过去,客户端通常先用 LIST 找到邮箱,再逐个发送 GETMETADATA。LIST-METADATA 能力把 METADATA 变成 LIST-EXTENDED 的返回选项,使名称、LIST 属性和指定注释可在同一条命令中返回。

对每个同时符合规范 LIST 模式和选择选项的可列出邮箱,服务器必须先返回 LIST,再返回一个或多个 METADATA。RFC 5464 允许把多个条目和值放在一条响应中,也允许拆成多条。因此,正确的记录键是“邮箱 × 条目”,不是响应行数,更不是命令数。

关键例外随后出现:如果服务器无法查到某邮箱的注释,RFC 9590 允许它丢弃相应 METADATA 响应,LIST 仍可返回带标签的 OK。OK 封闭了协议交换,却没有把未出现的数据补齐。

四个看似相同的空白

第一种是 NIL。RFC 5464 把它定义为条目没有值。RFC 9590 的示例里,foo 的颜色尚未设置,因此服务器仍返回 METADATA,只是该条目的值为 NIL。这里有明确回执。

第二种是查询缺失。对于本应取得元数据的邮箱,服务器因无法查询而没有发送 METADATA。这里没有 NIL,也没有条目值;证据链停在了查询之前或查询之中。

第三种是 \NonExistent。RFC 5258 说明,这个名称不指向现存邮箱,并蕴含 \NoSelect,但它仍可能因为层级或订阅关系出现在 LIST 中。RFC 9590 的例子给这样的名称返回 LIST,却不返回 METADATA。

第四种来自 SUBSCRIBED RECURSIVEMATCH。一个父名称可能仅因为它的后代满足选择条件而被列出,父名称自身并不满足选择条件,所以没有对应 METADATA。此处的缺省来自选择范围,而非注释状态。

产品界面可以把四者都画成灰色空格;审计数据不可以把它们压成同一个值。

覆盖率必须先定义分母

有效分母不是 LIST 行总数。它应当包含同时满足模式与选择条件、并且确实需要回答注释问题的邮箱,再与所请求条目做笛卡尔组合。明确返回的具体值和 NIL 都进入分子;仅为递归上下文出现的父名称不应膨胀分母;符合条件却缺少响应的邮箱—条目对也不能从分母中消失。

所以运行记录至少要保留命令标签、模式、选择选项、请求条目、LIST 名称与属性、预期邮箱—条目对、实际值、显式 NIL、缺失项、重试结果、会话或采集纪元,以及缓存动作。缺少这些原始回执,“元数据已同步”就只是给 OK 加上的解释。

RFC 5258 还提醒,后代邮箱可能在 LIST 之后、客户端访问之前被删除或改名。即便一次采集完整,它也不是永恒快照。RFC 9051 规定命令和状态机,并不证明缓存仍新鲜、界面已经呈现或用户已经看到。

注册表只证明共同词汇

IANA 登记了 LIST-METADATA 能力和 METADATA 返回选项。这证明标准化名称存在,便于互操作;它不证明部署率、具体产品的一致性或现场故障率。

“运行代码优先”要求把结论限定在可执行事实:服务器实际发出了哪些字节,客户端实际解析出了哪些名称、属性与条目值。最小共同规范负责定义这些字节的语义;重试预算、缓存时效、未知状态显示和业务阈值属于本地决定。把它们偷渡进 OK 的含义,正是把象征性成功误当成运行现实。

来源