Sun Mar 29 2026
...
FastComments 已准备好进入太空!
! 本文包含技术术语
新变化
每个 FastComments point-of-presence 现在在本地进行写入,并异步将其复制到所有其他节点。这将提供比以前系统更高的耐久性,同时也使某些区域的用户的内容审核工具更快,尽管有一些权衡。
“准备好进入太空”有点乐观,但这个想法是我们可以将 FastComments 部署到不同的行星,最终系统能够保持同步。然而,冥王星上的用户将不得不等待大约一天才能在他们即将到来的发票页面上看到更改,因为目前每个区域只能有一个主节点来聚合账单信息。
一些历史,为什么要改变
当 FastComments 最初推出时,我们采用了一种非常典型的架构。我们有一个代理层、一个应用层、一个数据库、一部分副本,后来又在不同区域和云提供商之间增加了副本以提高冗余性。
最终,我们将数据库副本迁移到我们大部分用户所在的所有区域,并且还在那里部署了应用程序,并创建了我们自己的 DNS 和代理系统(将在后面的博客中描述)来将请求路由到最近的应用节点。这种方式使得读取速度很快,但写入速度较慢,因为现在不必等待一次 HTTP 往返到底层,您只需要等待一次 HTTP 往返到附近的节点,而那个节点可能还会多次将数据库写入主节点。这并不好!
因此,为了解决这个问题,我们重构了应用程序的许多区域,以在函数参数中接受 readPreference,以便调用者可以决定他们在读取时可以接受多过时,并且在此基础上让更多的写入(例如在审核操作上写入审核员统计信息)实现火忘。这虽然不是理想情况,但显著提高了速度。
在全球运行 Mongo 过程中,我们遇到的一个问题是网络分割。如果足够多的节点被切断,读取会停止,因为每个节点都不确定是否可以提供读取服务。有一些解决方案,但极端情况会变得很复杂。这不是一个理论问题——这发生过很多次,导致我们在凌晨 3 点收到故障警报,最终我们对此感到厌倦,甚至尝试调整 Mongo,以应对复制集选举的不确定性,最长可达一分钟。不幸的是,像圣保罗到法尔肯施泰因的网络,在我们的一些托管提供商中,并不是很好。调优拥塞控制等帮助但并没有解决问题。
假设您可以接受某些权衡,理想的解决方案是能够在该节点(该节点具有不错的硬件、RAID 等,不太可能崩溃)本地接受写入并告知用户他们的数据已被保存。您还可以在该接入点拥有第二个节点作为热备份以提高耐久性。
因此,这就是我们最终达到的结果。俄勒冈、维吉尼亚、法尔肯施泰因、圣保罗、新加坡,都是各自的副本集并接受写入。欧盟的部署(尽管只有三个 PoP)有相同的行为。
它是如何工作的
这一部分在前面已经涵盖,但简而言之,它是 CRDT-lite。我们创建了一个代理(用 Rust 编写,因为当然了),它位于应用程序和 Mongo 之间,使其成为多主节点。该代理是对等感知的,管理检查点、复制、监控和初始同步。它是 Mongo 复制系统的多主节点替代方案,包括某些 DDL 命令。
与其他工具的区别在于它不跟踪 oplog。跟踪 oplog 或使用更改流是行不通的,因为它们只在写入后显示对象的最终状态,从而使得处理冲突变得不可能。您需要捕获每个 $set、$inc 操作并复制该操作本身。
这是一个特定领域的解决方案。它并不适用于所有产品。您可以说这是一种领域驱动设计 :)。对于我们而言,它起作用是因为我们从一开始就非常小心地仅对文档进行 $set 修改——例如,我们从未使用 Mongo 的 replaceOne。计数器也是一样。您绝对不会执行 SET VOTES = 5。相反,您会写 INCREMENT VOTES BY 5,因为这允许最终一致性。分布式锁则由不来处理。每个集群中只有一个节点有一个标志设置为运行 cron。虽然这看起来有限,但我们可以购买拥有数 TB RAM 的服务器,因此我们可以接受这种权衡,以降低风险和复杂性。
读取自己的写入
对于使用 API 的开发者,您应该能够像以前一样读取自己的写入(进行 API 调用以创建评论,然后列出评论并在该列表中查看新条目)。注意事项是您无法跨区域执行此操作。如果您的后端仅在一个区域内运行,比如 us-west,那么您应该能够读取自己的写入,除非在您的写入和读取之间,该节点出现故障,并且您的 DNS 缓存更新为指向下一个最近的节点。只要这种情况没有发生,读取自己的写入是可靠的。
测试与迁移
系统中大约一半的代码是测试工具、框架和测试。尽管如此,发布过程还是有点颠簸,造成了比期望更长的停机时间(欧盟 1 小时和全球 20 分钟),但我们很高兴能够通过这个里程碑,并感谢您们的耐心等待!
总结与对您的影响
FastComments 现在应该比以往更加快速和耐久,现在我们可以回去继续开发新功能 :)
干杯!
