优先级/严重性: 罕见但关键
在短暂的数据库错误后,网站最多会变得无法使用 30 分钟。由于缺少导致 JavaScript 错误的客户端设置,页面无法正确加载。
平台:
- 影响: 所有 Discourse 安装
- 观察到: 经历短暂数据库连接问题的生产环境
描述
实际结果
当发生短暂数据库错误时(连接超时、连接池耗尽、网络故障),错误会被缓存 30 分钟。在此期间:
- 网站无法正确加载 - 页面显示损坏或无法正常工作
- 由于缺少/无效的设置,会发生客户端 JavaScript 错误
- 每次请求都会记录“缓存中缺少 ‘client_settings_json\[git_version\]’ 的 Nil client_settings_json”
- 空的客户端设置会返回给浏览器
- 即使在数据库完全恢复后,这种情况仍然会持续
预期结果
当发生短暂数据库错误时,系统不应缓存该错误。数据库恢复后,下一个请求应成功获取并缓存客户端设置,并且网站应立即恢复正常运行。
可复现步骤
- 启动一个配置了客户端设置的 Discourse 实例
- 模拟数据库连接问题(例如,耗尽连接池、引入网络延迟或暂时阻止端口 5432)
- 发出一个触发 SiteSetting.client_settings_json 的请求(任何页面加载都会执行此操作)
- 观察错误:“生成 client_settings_json_uncached 时出错:[数据库错误]”
- 页面无法正确渲染,JavaScript 控制台显示与缺少设置相关的错误
- 恢复数据库连接
- 在接下来的 30 分钟内发出其他请求。尽管数据库健康,但仍会看到“缓存中缺少 Nil client_settings_json”错误
- 30 分钟后,缓存过期,网站最终恢复
用户影响
在此期间,网站实际上是关闭的:
- 页面无法正确渲染
- JavaScript 应用程序由于缺少配置而无法初始化
这会将“1 秒的数据库小故障变成 30 分钟的停机时间”。
根本原因
在 lib/site_setting_extension.rb 中,client_settings_json_uncached 方法会捕获异常并返回 nil。此 nil 会被缓存 30 分钟,导致网站中断。
建议修复
已提交一个 pull request 来修复此问题。该修复需要一个简单的更改,即重新引发异常而不是返回 nil。
它为何有效:
- 重新引发异常可防止 Discourse.cache.fetch 缓存错误
- 外部 client_settings_json 方法已经具有正确的异常处理程序,该处理程序返回“”(空字符串)而不缓存它
- 网站在数据库出现问题时仍可运行(尽管性能会下降)
- 数据库健康后,网站会在下一个请求时自动恢复
- 此修复可确保短暂的数据库错误不会导致 30 分钟的停机时间。
影响
此错误会影响所有遇到短暂数据库问题的 Discourse 安装:
- 流量高峰期间的连接池耗尽
- 应用程序和数据库之间的网络故障
- 数据库故障转移场景(例如,主/副本切换)
- site_settings 表上的锁争用
- 由于慢查询导致的查询超时
严重性: 上述任何常见、短暂的问题都会导致整个网站在 30 分钟内无法使用,即使根本问题在几秒钟内得到解决。
解决方法
如果您现在遇到此问题,可以手动清除缓存以恢复服务:
在 Rails 控制台中
Discourse.cache.delete(SiteSettingExtension.client_settings_cache_key)