摘要
- RFC 9738 允许 SEARCH、FETCH、STORE、MOVE 或 UID EXPUNGE 只处理 UID 较高的一段后返回
OK;MESSAGELIMIT同时说明处理上限与本次抵达的最低 UID。 - COPY 与 MULTIAPPEND 必须保持原子性,超限时不产生部分结果;其他命令可能已经造成真实变化,客户端必须按各自语义续跑并对账。
- CAPABILITY 中的数字是会话协作边界,不是严格执行已经全面启用、所有客户端已经适配、邮箱已经冻结或用户结果已经完整的证明。
结果末尾出现了 OK。这不是误码,也不是服务器自相矛盾。它表示本次命令已经成功处理自己实际覆盖的那一段。若同一行还带着 MESSAGELIMIT 1000 23221,它也同时表示:原请求中低于这道边界的部分还没有被本次调用覆盖。
很多系统只保留一个布尔值:成功或失败。RFC 9738 说明,面向大邮箱时,这种数据模型会主动制造误判。调用状态回答“刚才执行的工作是否成立”,完成状态回答“最初想覆盖的集合是否已经耗尽”。两者不能共用一个绿灯。
上限约束一次命令,不约束邮箱大小
服务器用 MESSAGELIMIT=N 声明单条 SEARCH、FETCH、STORE、COPY、MOVE 及其 UID 版本,以及 APPEND、UID EXPUNGE 最多处理多少封邮件。若只限制 COPY 与 APPEND 这两类保存操作,则使用范围更窄的 SAVELIMIT=N。规范建议 N 不低于一千。
这个数字不是邮箱容量,不是保留期限,不是配额,也不是单封邮件大小。它只描述一次命令愿意处理的邮件数量。
SEARCH 还有一个容易被忽略的细节:计算的是被检查的邮件,而不是匹配结果。服务器检查一千封后可能只返回十三个 UID。十三个匹配全部真实,但不能据此宣布整个目标区间只有十三个匹配。更旧的邮件尚未进入分母。
一旦返回 MESSAGELIMIT,服务器必须从最高 UID 向最低 UID 处理。响应中的最后一个 UID 是本次实际抵达的最低点。它适合作为下一段查询的接缝,却不是剩余邮件的清单、快照编号或数量证明。并发到达与 expunge 都可能改变集合;若 UIDVALIDITY 改变,旧接缝更不能继续解释新的邮箱代次。
OK 证明本次效果,不证明原始意图完成
FETCH 可以先返回最新一千封的属性,再以带限额的 OK 结束。SEARCH 可以返回这一千封中命中的集合。STORE 可能已经改变这一段的 flags。MOVE 可能已经把这一段复制到目标邮箱并从源邮箱 expunge。UID EXPUNGE 则可能已经永久移除这一段中带 \Deleted 的邮件。
这些都不是预演,而是真实效果。把它们一概记为“失败”,会丢掉已经发生的变化;把它们一概记为“完成”,会丢掉仍待执行的部分。
续跑方式必须服从命令语义。UID STORE 的下一次范围应排除响应给出的最低 UID。UID MOVE 可以重复原始 UID 集合,因为已移动的邮件已经不在源邮箱。使用 sequence number 的 MOVE 则必须重新计算范围,expunge 已经改变后续位置。UID EXPUNGE 可以重复同一 UID 参数,直到响应不再出现限额,或明确返回 NO、BAD。
因此,“自动重试”不是一个足够具体的设计。系统要知道上一次处理过哪些 UID、发生了什么效果、下一次为什么既不重复又不遗漏。如果把 UID 与 sequence number 混在同一个游标字段里,一次看似正常的续跑就可能作用于另一封邮件。
限额信号也不总在最终 tagged response 内。当服务器还要返回 EXPUNGEISSUED 时,后者必须占据最终 tagged OK,而 MESSAGELIMIT 放进 untagged NO。只读最后一行的解析器会保留“成功”,却扔掉“未完成”。完整凭据应包含命令 tag、全部 untagged response、返回数据、response code 与最后状态。
相同数字不会产生相同事务结果
COPY 与 UID COPY 必须是原子的。目标集合超限时,服务器返回 NO [MESSAGELIMIT …],一封也不能复制。MULTIAPPEND 同样需要全有或全无,过大的组不能先追加前一部分。
MOVE 没有这项整体原子保证。它可以先移动最新一段,再告诉客户端还有剩余。STORE 与 UID EXPUNGE 也能留下部分修改。于是,同一个 MESSAGELIMIT 数字至少对应两种完全不同的现场:没有任何效果的原子拒绝,以及已经发生效果、需要续跑的部分成功。
还有第三类:RFC 9738 不允许对 EXPUNGE、CLOSE 与 STATUS UNSEEN 套用这项邮件数量上限。这保留了它们在 IMAP 层的完整语义,但并不自动证明磁盘已经耐久写入、另一个客户端已经同步或读者已经看到变化。
RFC 9394 的 PARTIAL 能进一步说明差别。客户端若明确请求一个大于 N 的 PARTIAL 页,服务器应拒绝并且不做任何工作;不带 PARTIAL 的类似 FETCH 却可以隐式处理一段后返回部分成功。主动约定的页与服务器被动截断的一段,不能因为都“分批”就归入同一个状态。
$ 会把缺口保存下来
SEARCHRES 允许把一次 SEARCH 结果保存为 $。如果源 SEARCH 命中 MESSAGELIMIT,RFC 9738 要求保存的集合也必须截断。$ 精确地代表本次已检查范围内的匹配,但它不代表完整原请求的匹配。
危险往往在下一步出现。后续 COPY、STORE 或 SEARCH 使用 $ 时,集合可能已小于上限,整个调用可以干净地返回成功。新 transcript 中未必再次出现 MESSAGELIMIT,然而输入集合仍带着上一轮的缺口。后一次正确执行无法修复前一次未完成的分母。
因此,保存结果还要保存来源:邮箱、UIDVALIDITY、原始查询、目标区间、限额、最低处理 UID、时间以及“尚需续跑”的状态。只有变量名而没有这些信息,便利句柄很快会被误用为完整性证明。
RFC 9738 新增的 UIDAFTER 与 UIDBEFORE 能更方便地表达下一段范围。它们不会暂停新邮件到达,不会让已经消失的 UID 重新存在,也不会跨越邮箱代次。它们只是构造查询的工具,不是跨多条命令的事务锁。
声明上限可以先于严格执行
规范对旧客户端的处理尤其现实。若服务器突然严格限制大型邮箱,不理解扩展的客户端会只看到最新 N 封、得到错误的 SEARCH 计数,或在 COPY 时失败。规范明确指出,这相当于放弃这些客户端在大邮箱上的正常工作。
所以迁移可以分阶段。服务器先声明 MESSAGELIMIT,但暂时不严格拦截。理解扩展的客户端主动缩小命令,服务器负载已经下降;旧客户端仍可继续工作。之后,服务器可以声明一千这个软上限,却先把真正的硬上限设在未公开的一万。超出声明值的调用可被记录,用来观察客户端是否已经适配,再决定何时收紧。
这说明 CAPABILITY 是协作信号,不是执行测量。看到 MESSAGELIMIT=1000,可以要求新客户端以一千为设计边界,却不能只凭这个字符串断言服务器目前每次都在第一千封切断。真正的采用状态要从客户端请求、服务器响应与实际硬阈值共同观察。
软上限与硬上限可以并存,但必须分别记录。超过一千的命令,可能来自拒绝适配的新客户端、仍被宽容的旧客户端,也可能发生在严格执行启用之前。若报表把这些情况都叫“违规”,它会把过渡期变成虚假的合规故事。
完成需要给原始集合结账
可靠的全邮箱任务应先冻结“意图”,而不是假装冻结邮箱。记录开始时希望读取、修改、移动或删除的范围;随后为每次调用保存邮箱、UIDVALIDITY、命令、请求集合、原子类别、当时的 capability、返回 UID、已观察效果、untagged response、最终状态、限额与最低 UID。
收尾时至少要区分四个集合:开始时计划覆盖的邮件;实际检查或改变的邮件;续跑前已经消失的邮件;开始后新到达而不属于原范围的邮件。无法解释的差异必须保留为未决,而不能因为最后一次命令没有限额就自动清零。
相邻扩展也应保留各自职责。UIDBATCHES 用来规划有界 UID 区间;PARTIAL 请求明确的一页;MESSAGELIMIT 报告执行时遇到的单命令上限;SEARCHRES 保存结果集合;QRESYNC 协助同步变化。把它们都叫“批处理”,会让计划、请求、执行与同步之间的边界消失。
RFC 9738 最重要的不是让服务器少做一些,而是让“少做一些”仍能被客户端诚实地完成。服务器可以写下成功,因为这一页确实写完了;客户端必须继续保管整本书的目录,直到旧邮件也有明确去向。
来源
- https://www.rfc-editor.org/rfc/rfc9738.html
- https://www.rfc-editor.org/rfc/rfc9738.txt
- https://www.rfc-editor.org/info/rfc9738/
- https://datatracker.ietf.org/doc/rfc9738/
- https://datatracker.ietf.org/doc/rfc9738/history/
- https://www.rfc-editor.org/errata/rfc9738
- https://www.rfc-editor.org/rfc/rfc9051.html
- https://www.rfc-editor.org/info/rfc9051/
- https://www.rfc-editor.org/rfc/rfc9394.html
- https://www.rfc-editor.org/info/rfc9394/
- https://www.rfc-editor.org/rfc/rfc5182.html
- https://www.rfc-editor.org/info/rfc5182/
- https://www.rfc-editor.org/rfc/rfc5256.html
- https://www.rfc-editor.org/info/rfc5256/
- https://www.rfc-editor.org/rfc/rfc3502.html
- https://www.rfc-editor.org/info/rfc3502/
- https://www.rfc-editor.org/rfc/rfc6851.html
- https://www.rfc-editor.org/info/rfc6851/
- https://www.rfc-editor.org/rfc/rfc4315.html
- https://www.rfc-editor.org/info/rfc4315/
- https://www.iana.org/assignments/imap-capabilities/
- https://datatracker.ietf.org/doc/draft-ietf-extra-imap-messagelimit/
- https://heng.lu/running-code-primary-the-patch-needed-to-preserve-the-internet-original-design/
- https://heng.lu/minimum-initial-specification-localized-future-decision-voluntary-adoption-internet-coordination-system/
会员简报
档案背景详情
使用相应会员等级登录,即可解锁完整简报与来源注释。
仅限 Strategic Circle
Strategic Circle
所有读者均可浏览。加入并登录后可解锁档案简报。
加入 Strategic Circle仅限 Leadership Alliance
Leadership Alliance
符合条件的 IP 资产所有者和管理层可登录查看 Leadership Alliance 简报。
加入 Leadership Alliance
