实时更新主题在高活动下冻结

注意:我不确定这是否是 Discourse 的缺陷。我已尝试收集必要的证据,但至今未发现任何指向我们基础设施或配置的问题。我们在 Tappara.co 的配置尽可能保持原生(vanilla)。

观察到的现象:

  • 类似聊天的快速讨论主题停止自动更新。延迟 30 至 180 秒后,更新通常会恢复,并显示出冻结期间发布的帖子。

我们目前已知的情况:

  • 上一个赛季(最后一场比赛于 3 月进行)期间未出现此问题。
  • 我们运行的是稳定分支,并在 8 月进行了最新的主要版本更新。
  • 该问题在首场表演赛(流量/活动量中等)中立即被报告。
  • 此问题影响 iOS 和 Android 上的 Chrome,但在 Chromebook 上发生频率低得多。
    • 撰写本文时,我在 Android 手机上观察到冻结现象,而同一网络下的 Chromebook 上讨论则按预期流畅进行。
  • 体验因用户/客户端而异。不同用户在不同时间报告冻结。总体而言,我们在约 30 分钟内记录了约 300 条消息,用户报告了数十次冻结。大多数冻结似乎与比赛事件(进球、判罚)相关。

我已尝试排除的因素:

  • CloudFlare:我们曾在一场比赛中关闭 CF 缓存,但问题依然存在。
  • CPU 过载:CPU 使用率远低于限制,通常维持在 20-30% 左右。
  • 磁盘耗尽:磁盘 I/O 似乎也在正常范围内。我们使用的是 UpCloud 的 MaxIOPS SSD。

其他信息:

  • 比赛期间我运行了 Chrome 开发者工具,记录到了一些 429 错误,但对我而言,它们与冻结现象并无关联。
  • 最终用户并未收到关于 429 错误(限流)或极端负载的通知。更新只是冻结,随后又恢复。速率限制器最近是否有变更?我的理解是,触发速率限制时 UI 应显示提示?

这是一个非常棘手的问题,严重影响了比赛实况聊天功能。我们多年来一直运营此类聊天,但从未见过此类情况。

嗯,这不算是一个漏洞,而是一个特性 :sweat_smile:

当您的 Web Workers 因请求过多而不堪重负时,我们会在新帖子到达时自动更新主题时,在持久连接中引入延迟。

如果您看到此情况且 CPU 使用率较低(如报告所示),请增加 Unicorn 工作进程的数量,这应该能解决该问题。

我们已经在 6 核 VPS 上运行了 11 个 Unicorn 进程。如前所述,这种情况在上个赛季(最晚到 3 月)从未发生过,而现在即使流量中等也会出现。此外,在移动设备(尤其是 Android 上的 Chrome 浏览器)上,此问题比在桌面端频繁得多。

此外,我们之前也曾遇到过 CPU 资源耗尽的情况(在交易截止日)。

我错过了比赛期间的监控和服务器调试。我们将 web.ratelimited 参数翻倍,但这并未解决问题。

Inspector 捕获到多个 429 错误:

1. 请求 URL:

https://tappara.co/message-bus/3ed86765a67f4c31ba4053a0352ecaf5/poll

2. 请求方法:

POST

3. 状态码:

429

明天就是下一场比赛,我可以尝试调整 Unicorn 的数量。我们最多能扩展到多少个?最近的重大更新是否对此有所改变?

编辑:

我查看了统计数据,目前我们的活动量(页面浏览量、用户数)甚至低于春季的水平。每场比赛的聊天帖子数量与之前相同(约每 3 小时 900-1000 条)。

因此,出于某种未知原因,我们目前无法像 3 月份那样服务同样的用户群体。

我将在未来两周内对这个问题进行分析,改进需要一些时间。

太好了!能否确认一下,最近(6 个月内)是否有变更或回归问题导致了这一情况?

在此期间,我想我会为今晚的比赛给独角兽们加油助威,看看结果如何。如果我们需要任何支持,请随时告诉我。

@falco 独角兽的数量肯定不是关键。我已将今晚游戏的数量增加到 15。游戏主题较为平静,仅 700 条消息,但出现了持续卡顿现象。CPU 负载较轻,介于 5% 至 25% 之间。

随着我进一步调查,这看起来更像是一个问题和回归问题,但我的技能还不足以确定具体原因。

我认为客户端中存在一个 bug,它在遇到一个错误后基本上就停止更新了。我怀疑您遇到的情况是因为您的用户触发了速率限制。

正如我之前所说,本周我将着手调查并增强客户端的健壮性。这部分代码比较棘手,需要一些时间。

好的。在此期间,我会测试禁用全局限制器是否可行。我们周三就能知道结果。

调试这个问题让我开始思考实时讨论在用户体验(UX)方面的整体设计。许多社区都涉及现实生活中的事件,这些事件自然会将讨论“推”向类似聊天的快速对话模式。无论是股市、重大产品发布会,还是游戏(电子竞技或实体运动)……例子不胜枚举。

然而,在这种类似聊天的讨论文化中,帖子的质量参差不齐。另一方面,帖子往往有自然地在同一时间集中发布的倾向。想象一下,一场重要的冰球比赛正在进行,有人进球了:

  • 大部分帖子只是情绪化的反应、欢呼或叹息。
    • “进球啦!!!太棒了!”
  • 有些是信息性的:
    • “克罗斯比进球,企鹅队 1-0 领先。”
  • 少数人愿意花精力进行分析:
    • “克罗斯比打空门得分,这是在首都队一次漫不经心的前场逼抢后发生的,但看起来很像越位。首都队的教练应该对此提出挑战。”

现在,Discourse 作为一个快速平台(接近实时),这意味着即使一切运行顺畅,你也会在同一瞬间看到几十条帖子。对于读者来说,尤其是那些没有观看比赛但通过聊天主题关注赛事的人,这带来了用户体验上的挑战——在欢呼与叹息的洪流中很难找到那些信息性的帖子。在我们的论坛游戏聊天中,我们经常被问到_“比分是多少?”_,因为观看比赛的聊天者忘记了发布显而易见的事实,或者这些信息淹没在消息洪流中。

我不确定这在现实生活中会如何运作,但测试一下管理员是否可以设置讨论的节奏会很有趣。例如,每秒发布一条帖子。所有帖子都会被排队,但以设定的节奏在网站上发布。如果一个进球引发了 20 条反应帖,它们不会同时出现在主题中,而是分散在 20 秒的时间窗口内。这样是否更容易追踪并捕捉到相关信息?

当然,这可能会引发其他问题:如果新消息产生的速度持续超过发布速度,队列就会越来越长,聊天内容开始落后于现实世界。

不确定你是否理解了这一想法,甚至我自己也不确定这个想法是否可行。关键在于,实时聊天的用户体验是一个值得探讨的话题,并可能具有进一步开发的潜力?我理解 Discourse 的主要目标并不是打造一个聊天平台——已有其他软件可以胜任。但这类情况确实会自然发生。

我喜欢这个想法,但需要某种反向阴影封禁机制,确保用户能立即看到自己的帖子。否则,他们可能会重复发帖,甚至发三次,误以为论坛无法正常工作。

我刚刚合并了以下内容:

https://review.discourse.org/t/perf-backoff-background-requests-when-overloaded-10888/16227

它确保即使有 1000 人正在查看某个主题并不断发布帖子,也不会导致服务器崩溃。

客户端在这些情况下的表现现在要干净得多。

预见到 @ljpp 可能会问的问题,我目前仍在决定是否将其向后移植。这涉及 API 变更,且改动较大。如果我们决定向后移植……可能还需要几周时间。我需要在生产环境中在高负载下观察这一机制,而由于我们的托管资源非常充裕,类似这样的事件极少发生,因此要捕捉到这样的场景还需要一段时间。

绝地心灵控制 :wink:

  • 我们将尝试禁用速率限制器,看这是否是一个可行的变通方案。
  • 如果可行:我想下一个稳定版发布应该不远了。
  • 如果不可行,我们将查看 Beta 渠道。届时我们需要验证我们的 UI 自定义功能在更新后是否仍能正常运行。

我们是否有其他社区也在运行边缘分支(edge branches),并且有类似的聊天式讨论?

预计今年年底前会发布……所以我不认为它会超级快到来。不过,我们本周将发布另一个测试版!

我们所有的托管服务都运行在测试版上……所以是的,但我们拥有大量的容量。

我理解为什么这可能不是一个适合向后移植的候选项。我们刚刚禁用了 ratelimiter,明天就是下一场比赛,因此我们将大致了解这是否为那些不愿进入测试版的实例提供了一个可行的解决方案。

我们确实考虑在未来几个月内切换到测试版分支。不过也有一些其他顾虑——@rizka 指出印尼语翻译进度滞后(但他可能在本周晚些时候能够着手处理)。

@sam

遗憾的是,禁用 ratelimiter 完全没有帮助。那场比赛很无聊,83 名用户只发了 580 条消息。比赛期间还报告了多次卡顿。

在等待升级到边缘版本(edge release)之前,是否有任何潜在的漏洞利用或变通方法可以尝试?

所谓的“冻结”是客户端的一个漏洞,它只是未能正确处理错误情况。即使遇到一次速率限制错误,稳定版也会直接崩溃。

我想不出除了升级到测试版之外的任何临时解决方案(我们明天将发布新的测试版)。

我们的一位面向开发的成员提议调整以下变量。您怎么看——您认为这是一个可行的临时解决方案吗?

DISCOURSE_REJECT_MESSAGE_BUS_QUEUE_SECONDS: 0.2

我们尝试了以下变通方案:

DISCOURSE_REJECT_MESSAGE_BUS_QUEUE_SECONDS: 0.2

这显著减少了观察到的 slowdown 次数,但并未彻底解决问题。在游戏发生重大事件时,CPU 负载反而上升,在 55% 左右波动。

近期已进行了一些调整以缓解“冻结”问题:当服务器过载时,客户端将退避并等待。

不过,根本的解决方案可能是升级更大的服务器并增加更多 Unicorn 工作进程。关于服务器容量与工作进程数量的推荐配置,请参阅 discourse-setup 脚本。

我对此表示怀疑……在高回复负载下,问题在于:在新设计推出之前,我们可能引发流量洪峰,从而触发 max_reqs_per_ip_per_10_seconds 等速率限制。要应对这种负载,需要庞大的资源。

试想一下:

  • 30 名用户在 10 秒内发布回复;
  • 100 人正在浏览该话题;
  • 服务器需要能够处理 3000 次 GET 请求,以每次获取一条帖子;
  • 如果任何一个请求因任何原因失败,用户界面就会卡死并显示为异常。

新设计非常干净地解决了这一问题:负载会自动退避,若出现积压则请求会被批量处理,界面不会卡死,等等。

我无法想象旧设计能扩展到支持 100 个并发用户并在 10 秒内处理 30 条回复。

而当前的修订版设计,完全可以支持 1000 个并发用户浏览一个话题,并在 10 秒内处理 30 条回复。