TechFoco Logo
首页专题归档站点地图
© 2026 TechFoco. All rights reserved.
文章归档专题网站地图站点地图 XML爬虫规则
  1. 首页
  2. /
  3. 返回归档
  4. /
  5. 只重建必要部分:从 Cloudflare 的 DNS 改造看增量系统如何证明正确

只重建必要部分:从 Cloudflare 的 DNS 改造看增量系统如何证明正确

2026年11月06日•TechFoco 精选

只重建必要部分:从 Cloudflare 的 DNS 改造看增量系统如何证明正确

更新一条记录,系统要重新计算多少东西?这个问题在数据量不大时几乎没有存在感。读取完整状态,重新生成结果,再写入发生变化的部分,往往就是最容易实现和排查的办法。但当对象越来越大、变更越来越密,原本可以忽略的工作开始占用内存和调度资源,还可能让彼此无关的任务一起等待。此时,难题不只是把计算切小,而是怎样证明没有因为少做一步,悄悄留下错误结果。

Cloudflare 工程师 Alex Fattouche 于 2026 年 11 月 2 日发表了 DNS 构建改造的公开复盘。原文所说的构建,是把已存储的 DNS 记录与区域配置转换成权威服务器可用的数据,而不是递归解析器读取缓存的过程。团队为不同变更选择能够证明安全的工作范围,并让同一区域的等待任务在统一调度器中合并。按照该文的生产观测,构建部署的峰值工作集内存从约 79 GiB 降至 12 GiB;在同时缩短重建间隔与聚合器等待后,变更到写完分布式存储的延迟中位数从约 7.7 秒降至 3.3 秒。这是特定部署及指标边界内的结果,不能当作 DNS 查询延迟,也不等于所有用户在边缘看到变化的时间。

以下是对增量计算与事件调度的独立分析。文中的假设场景用来比较设计选择,并非对 Cloudflare 内部业务的复原。

少写与少算,是两个不同的问题

一种常见误判是,看到最终只写了一小部分键,就认为整个链路已经是增量的。实际可能是生成器读取了全部记录,为每条记录构造中间状态,再对照之前的输出,最后只提交变化。这样确实减少了写入与下游传播,却没有减少读取、编码、比较和内存压力。若这些环节才是瓶颈,继续压缩写入批次就不太可能解决问题。判断成本应把一项更新从入口到结束的工作分解,并说明每一环节的成本随什么变量增长。

在区域大小悬殊的系统里,同样叫一次更新,工作量可能相差几个数量级。看总事件频率只能衡量请求压力,看不出单次任务占了多少内存、做了多少读取,或把调度队列拖住多久。更有用的观察是按变更类别、对象规模与工作类型分组,区分普通更新、共享元数据变更以及首次生成或全面失效。若某类事件出现很少,但会在最大对象上触发全量计算,它仍可能主导资源预算。

假设一个公开文档站有一份包含大量页面的索引。一篇文章的标题更新后,只替换一个索引条目似乎很自然。但如果生成器每次都重新解析全部正文,再比较最后写入的数据,它节省的仍只有磁盘写入。要减少计算,必须在读取之前知道哪些输入可能影响标题条目、哪些输出不会变化。这个问题与选择消息框架或换更快的机器无关,首先是一道依赖分析题。

也有反例。对象小、变更稀疏、全量实现稳定,且不存在长任务阻塞时,增量路径反而可能因为维护两套语义而增加总成本。全量方案可以作为正确性的参照,也能提供简单的恢复方式。只有当成本测量显示重复读取或计算已经妨碍可靠交付,而且能解释范围规则,才有理由承担额外复杂度。

用可比较的输出定义正确性

说某次变更只影响一项记录,不能只看输入名称。一个字段可能改变同一对象的多个派生结果;删除输入可能需要删除已经不存在于新数据里的旧输出;调整发布条件还可能改变哪一组结果可以被外部看到。因此,一个可靠的增量方案需要明确说明:新增了哪些内容,替换了哪些内容,哪些旧内容应被清除,哪些共享索引也要变化。

Cloudflare 的复盘给出了一个有力的判断标准:局部构建写入的键应与全量构建在相同状态下产生的键逐字节一致。这个标准的普适价值,在于将正确性与特定算法脱钩。团队可以改变读取策略、任务分组与代码结构,但必须解释变更结果是否仍等价。还应同时比较删除后的缺失状态;只比较仍然存在的键,无法发现旧值本应被清理却遗留的问题。

在假设文档站中,移除一页的旧标签,可能让这个页面不再出现在一个分类索引中。如果验证只核对新标签下能找到它,那么旧分类仍会指向这页,错误没有被发现。更扎实的测试要检查整个被影响的结果集合:新分类包含该页,旧分类不再包含该页,未涉及的分类保持一致。对于分页或排序结果,还应验证条目移动引发的边界变化。重要的是从读者可观察的状态推导检查,而不是从实现里挑几个容易比较的字段。

这里也有实务上的成本。生产中不可能每次运行全量构建来校验局部构建,否则省掉的工作又做了一遍。可以在小对象上持续抽样对照,在压测或回归环境重放罕见变更,并在变更分类代码时扩大对照范围。抽样不能证明所有情形均正确,因而还要保留显式的不变量、可审查的依赖映射,以及发现差异后的修复路径。

事件说明发生过什么,执行必须回答现在该做什么

异步系统的一个容易被忽略的事实,是事件抵达与数据可读往往经过不同的链路。消费者可能先收到变更通知,读到的副本却还没有复制相应事务。如果此时按照旧状态生成输出,然后宣告任务完成,就可能用一次看似成功的操作覆盖真正应传播的更新。队列重试只能处理被识别为失败的情况;如果系统根本没有判断读取落后,重试机制不会自动发现它。

Cloudflare 的设计把事件用于生成保守的初步计划,再在执行前读取当前发布状态并判断副本是否足够新。这里有一个区分:事件适合表达触发原因和需要检查的范围,读取适合确认实际要发布的结果。把二者混为一谈,会让团队在已排队很久的任务中依然相信旧的权限、旧的路由或旧的配置。二阶段判断并不是机械地读两次,而是分别回答两个不同的问题。

假设一个搜索服务根据内容更新事件重建索引。事件产生时某页是公开的,但轮到它处理时页面已经撤下。若处理器信任事件携带的公开标记,就可能重新把它加入索引;若只读取当前页面却没有检查副本进度,仍可能看到撤下前的旧值。实现需要明确能接受什么版本的读取,如何识别版本不足,以及撤销状态如何覆盖早先等待的任务。若外部动作不可重复,尤其不能只依靠时间上看起来合理来推断顺序。

固定等待时间仍有用途:多数副本很快追上,短暂缓冲可以减少无谓的失败重试。但固定等待无法证明数据一定已经可读,尤其是在复制故障或负载尖峰时。等待是提高常见路径成功率的措施,新鲜度检查才是正确性条件。二者分离后,团队才知道缩短等待会影响什么:可能增加拒绝和重试,却不应悄悄改变允许写入的结果版本。

合并计算可以,消息的责任不能合并掉

同一个对象短时间内收到多项更新,合并任务常能避免重复读取与调度。最直观的例子是,两个等待任务分别刷新不同名称,合起来可以一次读入相关数据并写回各自结果。但合并只有在结果能够覆盖原始义务时才安全。若其中一项变更影响共享元数据,合并后的范围要保留它;若某项要求全量更新,普通局部计算不能把这个要求冲掉。

这也解释了为什么工作清单与消息确认应分别记录。假设十条通知合并为一次构建,最终真正写入的键只有两个;这并不意味着其余八条通知可以在构建成功之前确认。消息的义务是,它所对应的变更已经被安全地反映在最终结果里,或已被更晚且合法的状态覆盖。任务重试可以压缩计划,但仍需知道哪些通知在等待同一结果。错误地丢掉确认关系,可能导致已经完成的消息反复重放,也可能让未完成的变更永久丢失。

对于有序分区,某项任务拖延还可能阻塞可提交的连续进度,即便后面的任务已经处理完成。为此,应分别观察处理完成位置、已持久提交位置与最老未完成项,而不是只盯着平均处理速度。否则,一个大对象堵住提交后,团队可能以为是整个消费者池不够快,不断加并发,却没有改变前方那项任务无法结束的事实。

合并的另一个局限是实时性。等待更多事件可能提升资源利用率,但也把第一个事件放进等待窗口。批次要多大,不能只由读取成本决定,还取决于业务能接受的传播延迟。对于失效、撤销等高影响变更,应明确是否需要独立优先级。否则,原本为处理吞吐设计的缓冲,可能在故障时让最需要立即传播的状态滞留。

共享目录是另一层状态,不能随便补丁式更新

读服务经常要先查一份索引或目录,再找到具体数据。在这种结构里,一个很小的目录键可能比成千上万条内容更危险:如果它指向尚未完整写入的位置,读服务会看到空值或混合状态;如果更新者从旧目录打补丁,也可能把其他任务的较新更新覆盖掉。需要分别问清楚,内容写入先于目录是否足够、两个更新是否会竞争、旧引用何时可以安全删除。

原文中,同一公开域名可能对应多个内部区域对象,元数据记录其中被选为可发布的对象。团队在需要更新共享元数据时依据当前视图重建,并先写入目录引用的触发区域记录。这说明最小范围不是按输入行简单划分。普通记录与共享目录属于不同的影响层;即便两个任务写入的数据互不相同,也可能在选择可见结果的目录上产生交叉。可见性规则比单纯的键是否相同更早决定协调边界。

以假设的软件制品仓库为例,版本指针指向一组文件与校验数据。如果指针先更新,下载者可能取得新版本的名字和旧版本的内容。更稳妥的顺序通常是先发布可完整读取的版本,再变更指针;但在多写入者环境下,顺序仍不能替代对旧版本的比较或写入竞争的处理。如果操作被打断,恢复程序需要能辨认哪些文件已经准备好、当前指针属于哪个版本,而不是仅凭时间戳推测。

并非所有系统都需要跨许多对象的原子提交。若版本可以独立读取、延迟回收不会泄露权限,先写新内容再切换可见指针可能已经足够。若撤销涉及权限回收,允许短时混合状态的条件就更严格。决策应由外部能观察到哪些不一致、它们的后果如何决定,不能把另一家平台的发布顺序当作普适合同。

为无法完成的全量回退准备真正可行的替代

遇到未知事件就回退全量构建,是一个有用的保守默认。但只有当全量路径在最差对象上仍能结束,它才是有效的保险。大对象所需的内存超过可用资源时,一再重试会同时消耗机器和队列位置,既没有提高正确性,也会扩大影响范围。Cloudflare 公布的方案在超大区域上存在明确取舍:超过固定记录数阈值时,不再试图运行无法完成的全量构建,按已知范围处理,并在少数需要时人工重建具体名称。这项例外本身值得关注,因为它说明保守规则也要经过资源约束检验。

其他系统可以采用不同策略。若生成结果可以按互不影响的片段计算,一种选择是流式构建,再通过版本切换使整体结果生效。若数据模型允许安全恢复,可以让失败的大任务进入独立处理路径,避免阻塞常规分区。也可以减少全量重建依赖的中间状态,把差异比较变成按块扫描。但每种方法都要回答何时产物算完整、跨块引用如何核对、运行中断后从哪里继续。将一个不可结束的大任务拆成许多小任务,未必消除了整体一致性问题。

同样,人工介入不是只写一行告警。人工操作应知道未自动处理的影响范围、应该使用哪个数据版本、有哪些检查能证明缺口已经补齐。若系统只有一种模糊的失败状态,值班人员可能不得不回到日志里猜测哪些内容可信。异常路径越罕见,越需要在正常时期用可重复的演练验证它是否可用。人工修复的实际含义,应是补足一段机器尚不能安全自动完成的工作,而不是把无法解释的副作用转交给人。

性能数字要说明计时从哪里开始,到哪里结束

原文给出的中位数变化包含对等待策略的调整,而不仅是构建器跑得更快。这个区别关系到怎样复制经验。若优化缩短单次计算,却不调整排队、合并与读副本缓冲,端到端延迟未必变化;反过来,缩短等待可以让数字更好看,但如果没有新鲜度检查,就可能以隐蔽错误为代价。严谨的比较要同时记录处理耗时、调度等待、写入完成及需要时的外部传播,而不是把任何一个指标统称为延迟。

内存也需要解释计量口径。峰值工作集是一个部署在观察窗口里的资源压力,不是每条记录的成本。它会随并发、对象分布和观察时段变化。比较前后,应确认采集方式相同,并同时关注高峰是否由单个大对象、多个并发任务,还是失败重试引起。这样才能判断减小任务范围是否解决了根因,或者只是恰好没有遇到同样的负载形状。

百分位统计还会受采样与桶边界影响。若直方图只有很宽的区间,某些仪表盘会把桶内样本按近似分布估算,得到看似精确却偏离真实的位置。评审性能改造时,可将采集方式与指标含义一起记录,并用多种切分视角核对,例如按对象规模、变更类型与是否重试分组。总体中位数适合描述典型路径,但它无法替代最慢路径、失败率与错误结果的独立观测。

从证据开始设计,而不是从缓存或队列开始

一种实用的评估顺序是,先列出系统必须生成的可见结果与必须清理的旧状态,再追踪每类输入变更可能影响哪些输出。随后为读取新鲜度、执行时权限或发布资格定义约束。最后才决定如何分组、并行、重试和提交进度。这样,读者可以针对任何一次更新回答:为什么做这些工作,为什么不用做更多,如何证明没有做少。

如果一个团队尚不具备上述证据,可以先让全量方案继续承担生产结果,只用影子计算测量局部方案会改动的范围,并把差异作为学习材料。对于同一份输入,局部与全量结果能否比较,要先保证观测到的是相同状态;如果比较的是两份不断变动的快照,差异可能根本不来自算法。把这个前提写明,比立刻设计一套复杂的失效传播框架更有价值。

渐进采用也应该预先设置退出条件。例如,只有在某类事件的依赖明确、对照通过、删除与竞态已覆盖,并且现场出现未知事件时能降级,才把该类事件接入增量路径。上线后若差异集中出现在某个领域规则、某种拓扑变更或某批旧格式事件,可以把受影响类别切回更保守的流程。这样,回退作用在一个可解释的边界上,而不必在全部启用和全部停用之间二选一。

另一项检验是升级兼容性。事件格式往往比某次部署存活得更久,重放时可能遇到旧生产者留下的通知。假设旧格式没有记录改动字段,新消费者就不应将字段缺失解释为没有影响;它可以走较宽的计算范围,或等待可用的版本证据。对于回滚,也需要明确旧消费者是否理解新格式,必要时先兼容读取,再逐步启用新的生产方式。这样才能把增量逻辑放进可维护的演进过程,而不是依赖所有组件在同一时刻切换。

Cloudflare 的案例并不意味着所有异步系统都应该采用同样的调度器。它更有价值的提醒是:减少工作以前,先让系统能够描述工作的范围、数据的版本和完成的证据。真正稳健的增量系统,知道何时可以少做,何时必须多做,以及当两种选择都不成立时怎样报告异常并恢复正确状态。随着规模扩大,节省的不是一个抽象的百分比,而是那些已经能够证明没有必要重复执行的工作。

公开原文:https://blog.cloudflare.com/dns-targeted-zone-builds/

继续阅读

较新文章

OpenSSH 10.6p2 修复特定 musl 配置的认证绕过

较早文章

Nexus Mods 更新模组上传的 AI 披露要求

相关文章

查看更多

ARTEX 原仓库关闭后,维护方说明社区分支的许可边界

ARTEX 原仓库关闭后,维护方说明社区分支的许可边界 维护方表示,10 月 8 日之后出现的社区分支不属于官方维护版本,既有开源版本仍按 AGPL 3.0 处理。

2026年11月07日

研究者公开 iOS 27 键盘预测文本过滤的逆向分析

研究者公开 iOS 27 键盘预测文本过滤的逆向分析 研究从 iOS 27 键盘本地数据中提取多语言词条,发现部分词语和短语不会被预测文本或滑动输入推荐。

2026年11月07日

Google 公布怀俄明州数据中心的混合供电方案

Google 公布怀俄明州数据中心的混合供电方案 项目计划将 702 兆瓦天然气燃料电池与碳捕集及地质封存结合,同时从天然气发电机组采购电力。

2026年11月07日

PowerToys 0.102 将取色器和文本提取器迁移到 WinUI 3

PowerToys 0.102 将取色器和文本提取器迁移到 WinUI 3 取色器的覆盖层、编辑器和放大镜随迁移更新,现有快捷键和设置可沿用。

2026年11月07日

豆包工作个人版取消 5 小时滚动额度

豆包工作个人版取消 5 小时滚动额度 从 11 月 6 日起,个人版用户可在每周额度内集中运行长任务,无需再分摊到多个 5 小时窗口。

2026年11月07日

研究者公开微信输入法本地词库提取脚本

研究者公开微信输入法本地词库提取脚本 脚本从 Windows 版安装目录中的词库文件解析条目,适合在同一版本上复核本地数据结构。

2026年11月07日