我在调查员工用户的初始页面加载性能时,注意到了一种类似于“每个设置一次查询”(N-per-setting)的模式。
在我的站点上,普通非员工账户加载 /latest 的速度明显快于我的管理员/员工账户。在使用 Rack Mini Profiler 对员工请求进行性能分析时,我发现 SiteSerializer#category_types 似乎是通过逐个检查每个 SiteSetting 来确定其是否已被覆盖,从而构建类别类型配置元数据的。
相关路径似乎是:
SiteSerializer#category_types
→ Categories::TypeRegistry.list
→ 类别类型 metadata
→ resolved_configuration_schema
→ site_setting_overridden?
→ SiteSetting.provider.find
当前代码:
特别是,site_setting_overridden? 调用了:
SiteSetting.provider.find(setting_name.to_sym).present?
而 resolved_configuration_schema 在遍历配置架构中的 SiteSettings 时调用了该方法。
随后,SiteSettings::DbProvider#find 会对指定的设置执行单独的查询:
在性能分析器输出中,我观察到如下单独的查询:
SELECT name, data_type, value
FROM site_settings
WHERE name = 'discourse_post_event_allowed_on_groups';
SELECT name, data_type, value
FROM site_settings
WHERE name = 'use_local_event_date';
SELECT name, data_type, value
FROM site_settings
WHERE name = 'show_filter_by_solved_status';
SELECT name, data_type, value
FROM site_settings
WHERE name = 'topic_voting_show_who_voted';
在捕获的请求中,有 16 个查询符合这种 site_settings WHERE name = ... 模式。这些查询针对的是不同的设置名称,因此我并不是说同一个查找只是被简单地重复执行;相反,这看起来像是针对类别类型配置架构中代表的每个 SiteSetting 执行一次数据库查找。
这对员工用户尤其相关,因为 category_types 仅包含在员工的数据中:
我的性能分析还显示,插件提供的类别配置也参与了此路径,包括 Discourse Solved、日历/事件相关设置以及 Topic Voting 设置。
作为对比,在相同的 Discourse 安装环境和连接下,/latest 的预热加载时间大约为:
管理员/员工账户:
DOMContentLoaded:大约 0.77–1.03 秒
初始 HTML:解码后大约 2.08 MB
普通账户:
DOMContentLoaded:大约 0.51 秒
解码后的资源总量:大约 1.50 MB
在后续测试中,普通账户的 PWA 加载速度也至少与 AMD Developer Community PWA 一样快,因此这似乎不是一般的源站/CDN 性能问题。
我还遇到了一次不寻常的员工安全模式(Safe Mode)请求,其中 layouts/application 产生了 1,362 个 SQL 查询,耗时约 2.9 秒。我不认为上述每个 SiteSetting 的查询能解释所有这些查询,因此我将此情况单独处理,而不是声称这是导致整个异常值的原因。
对于 resolved_configuration_schema 使用的已覆盖设置状态,是否值得将其一次性获取/批量处理,而不是为每个 SiteSetting 单独调用 SiteSetting.provider.find?
例如,DbProvider 已经有一个 all 方法,尽管我并不是说直接使用 all 一定是正确的实现方式——主要是想知道在这里避免每个设置一次数据库查找是否值得。
我检查了当前 main 分支的相同路径,看起来每个设置的 provider.find 行为仍然存在。