Roblox 停摆 73 小时:祸根是一个新开的功能,加上一个老数据库的“空洞”
配图:AI 生成的概念示意图,非论文原图、真实现场或实验数据。
TL;DR:2021 年 10 月底,Roblox 全平台中断约 73 小时。公司事后复盘称,直接原因是服务发现组件 Consul 的新“流式”功能在高负载下发生争用,同时 Consul 底层的 BoltDB 在这一负载下出现性能病态。排查期间,监控本身也依赖 Consul,工程师一度“看不见”系统。Roblox 称没有用户数据丢失,也没有未授权访问。
这是 2021 年的旧事,不是今天的新闻。Roblox 在 2022 年 1 月发布了复盘文章,下面的内容均出自这份复盘。
背景
事发时 Roblox 约有 5000 万日活用户,后台有 1.8 万多台服务器、17 万个容器。这些服务靠 HashiCorp 的 Consul 互相“找到”对方,也用它存放部分键值数据。Consul 集群由 5 个投票节点和 5 个只读节点组成,几个月前刚从 1.9 升级到 1.10。
事情经过
10 月 27 日,Roblox 在一项流量路由服务上启用了 Consul 的流式功能,同时把节点数增加了 50%。次日(10 月 28 日)下午,Consul 出现高 CPU,在线玩家随后跌到平时的一半,之后平台整体不可用。
接下来是一连串走错的路:怀疑硬件故障,换掉一台机器;怀疑流量过大,把集群换成 128 核机器(原来是 64 核);用快照重置 Consul 状态;削减负载、把健康检查从 60 秒放宽到 10 分钟再分批重启。这些都没能让集群稳定,后来还把机器换回了 64 核。
真正的原因
据复盘,转折点是 10 月 30 日下午关闭所有 Consul 系统上的流式功能,写入延迟随即大幅下降。流式功能在读写都很高时,会在单个 Go channel 上争用并阻塞写入,复盘还提到 128 核双路服务器的 NUMA 架构让争用更严重。
第二个问题藏得更深:Consul 的 Raft 日志存储用的是 BoltDB,它在快照之后不会收缩磁盘文件。当时日志文件有 4.2GB,真实数据只有 489MB;每次追加日志,都要重写一个含近百万空闲页 ID 的空闲列表,导致 Raft 写入耗时约 2 秒。Roblox 用现有工具压缩数据库后,这个问题得到解决。
为什么这么久
另一个拖后腿的因素是遥测系统也依赖 Consul,出事时观测数据一并缺失。找到原因后,还要重建缓存、清理过期的调度数据,再用 DNS 分约 10% 一档放玩家回来。10 月 31 日 16:45(复盘采用太平洋时间),玩家全部恢复。
后续
Roblox 去掉了遥测对 Consul 的循环依赖,加了 Consul/BoltDB 告警,把关键服务拆到独立的 Consul 集群,新建了地理上分离的数据中心,并计划迁移到 BoltDB 的后继者 bbolt。流式功能则要等重新设计并在规模下测试后才会再启用。
这起事故的教训很朴素:一个看似无关紧要的组件,会同时是几乎所有服务的“电话簿”和观测系统的地基,它出事就是全局出事。