Staff SiteSerializer category_types performs one DB query per SiteSetting in configuration schemas

I noticed what looks like an N-per-setting query pattern while investigating initial page-load performance for staff users.

On my site, an ordinary non-staff account loads /latest noticeably faster than my admin/staff account. While profiling the staff request with Rack Mini Profiler, I found that SiteSerializer#category_types appears to build category-type configuration metadata by checking each SiteSetting individually to determine whether it has been overridden.

The relevant path appears to be:

SiteSerializer#category_types
Categories::TypeRegistry.list
→ category type metadata
resolved_configuration_schema
site_setting_overridden?
SiteSetting.provider.find

Current code:

In particular, site_setting_overridden? calls:

SiteSetting.provider.find(setting_name.to_sym).present?

and resolved_configuration_schema calls that while iterating the SiteSettings in the configuration schema.

SiteSettings::DbProvider#find then performs an individual query for the named setting:

In the profiler output I saw individual queries such as:

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';

There were 16 queries matching this site_settings WHERE name = ... pattern in the captured request. These were for different setting names, so I am not suggesting that the same lookup is simply being repeated; rather, it appears to be one DB lookup per SiteSetting represented in the category-type configuration schemas.

This seems particularly relevant to staff users because category_types is only included for staff:

My profiling also showed plugin-provided category configuration participating in this path, including Discourse Solved, calendar/events-related settings and Topic Voting settings.

For comparison, on the same Discourse installation and connection, a warm /latest load was approximately:

Admin/staff account:
DOMContentLoaded: roughly 0.77–1.03 s
Initial HTML: roughly 2.08 MB decoded

Ordinary account:
DOMContentLoaded: roughly 0.51 s
Total decoded resources: roughly 1.50 MB

The ordinary-account PWA also ended up loading at least as quickly as the AMD Developer Community PWA in subsequent testing, so this does not appear to be a general origin/CDN performance issue.

I also had one unusual staff Safe Mode request where layouts/application produced 1,362 SQL queries and took about 2.9 seconds. I do not think the per-SiteSetting queries above account for all of those queries, so I am treating that separately rather than claiming this is responsible for the entire outlier.

Would it make sense for the overridden-setting state used by resolved_configuration_schema to be fetched/batched once, rather than calling SiteSetting.provider.find separately for each SiteSetting?

For example, DbProvider already has an all method, although I am not suggesting that using all directly is necessarily the correct implementation - mainly wondering whether avoiding the N-per-setting database lookups here would be worthwhile.

I checked the same path at current main and it appears that the per-setting provider.find behaviour is still present.

2 Likes