client_settings_json 中缓存的数据库错误 30 分钟 - 网站加载失败

优先级/严重性: 罕见但关键
在短暂的数据库错误后,网站最多会变得无法使用 30 分钟。由于缺少导致 JavaScript 错误的客户端设置,页面无法正确加载。

平台:

  • 影响: 所有 Discourse 安装
  • 观察到: 经历短暂数据库连接问题的生产环境

描述

实际结果
当发生短暂数据库错误时(连接超时、连接池耗尽、网络故障),错误会被缓存 30 分钟。在此期间:

  • 网站无法正确加载 - 页面显示损坏或无法正常工作
  • 由于缺少/无效的设置,会发生客户端 JavaScript 错误
  • 每次请求都会记录“缓存中缺少 ‘client_settings_json\[git_version\]’ 的 Nil client_settings_json”
  • 空的客户端设置会返回给浏览器
  • 即使在数据库完全恢复后,这种情况仍然会持续

预期结果
当发生短暂数据库错误时,系统不应缓存该错误。数据库恢复后,下一个请求应成功获取并缓存客户端设置,并且网站应立即恢复正常运行。

可复现步骤

  1. 启动一个配置了客户端设置的 Discourse 实例
  2. 模拟数据库连接问题(例如,耗尽连接池、引入网络延迟或暂时阻止端口 5432)
  3. 发出一个触发 SiteSetting.client_settings_json 的请求(任何页面加载都会执行此操作)
  4. 观察错误:“生成 client_settings_json_uncached 时出错:[数据库错误]”
  5. 页面无法正确渲染,JavaScript 控制台显示与缺少设置相关的错误
  6. 恢复数据库连接
  7. 在接下来的 30 分钟内发出其他请求。尽管数据库健康,但仍会看到“缓存中缺少 Nil client_settings_json”错误
  8. 30 分钟后,缓存过期,网站最终恢复

用户影响

在此期间,网站实际上是关闭的:

  • 页面无法正确渲染
  • JavaScript 应用程序由于缺少配置而无法初始化

这会将“1 秒的数据库小故障变成 30 分钟的停机时间”。

根本原因

在 lib/site_setting_extension.rb 中,client_settings_json_uncached 方法会捕获异常并返回 nil。此 nil 会被缓存 30 分钟,导致网站中断。

建议修复

已提交一个 pull request 来修复此问题。该修复需要一个简单的更改,即重新引发异常而不是返回 nil。

它为何有效:

  1. 重新引发异常可防止 Discourse.cache.fetch 缓存错误
  2. 外部 client_settings_json 方法已经具有正确的异常处理程序,该处理程序返回“”(空字符串)而不缓存它
  3. 网站在数据库出现问题时仍可运行(尽管性能会下降)
  4. 数据库健康后,网站会在下一个请求时自动恢复
  5. 此修复可确保短暂的数据库错误不会导致 30 分钟的停机时间。

影响

此错误会影响所有遇到短暂数据库问题的 Discourse 安装:

  • 流量高峰期间的连接池耗尽
  • 应用程序和数据库之间的网络故障
  • 数据库故障转移场景(例如,主/副本切换)
  • site_settings 表上的锁争用
  • 由于慢查询导致的查询超时

严重性: 上述任何常见、短暂的问题都会导致整个网站在 30 分钟内无法使用,即使根本问题在几秒钟内得到解决。

解决方法

如果您现在遇到此问题,可以手动清除缓存以恢复服务:

在 Rails 控制台中

Discourse.cache.delete(SiteSettingExtension.client_settings_cache_key)
2 个赞

抓得好,我会着手修复这个问题。

5 个赞

看起来它已被合并,非常感谢。

2 个赞