discourse/spec/components
Alan Guo Xiang Tan cd64e88711
PERF: Improve database query perf when loading topics for a category. (#14416)
* PERF: Improve database query perf when loading topics for a category.

Instead of left joining the `topics` table against `categories` by filtering with `categories.id`,
we can improve the query plan by filtering against `topics.category_id`
first before joining which helps to reduce the number of rows in the
topics table that has to be joined against the other tables and also
make better use of our existing index.

The following is a before and after of the query plan for a category
with many subcategories.

Before:

```
                                                                                                       QUERY PLAN

-------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
 Limit  (cost=1.28..747.09 rows=30 width=12) (actual time=85.502..2453.727 rows=30 loops=1)
   ->  Nested Loop Left Join  (cost=1.28..566518.36 rows=22788 width=12) (actual time=85.501..2453.722 rows=30 loops=1)
         Join Filter: (category_users.category_id = topics.category_id)
         Filter: ((topics.category_id = 11) OR (COALESCE(category_users.notification_level, 1) <> 0) OR (tu.notification_level > 1))
         ->  Nested Loop Left Join  (cost=1.00..566001.58 rows=22866 width=20) (actual time=85.494..2453.702 rows=30 loops=1)
               Filter: ((COALESCE(tu.notification_level, 1) > 0) AND ((topics.category_id <> 11) OR (topics.pinned_at IS NULL) OR ((t
opics.pinned_at <= tu.cleared_pinned_at) AND (tu.cleared_pinned_at IS NOT NULL))))
               Rows Removed by Filter: 1
               ->  Nested Loop  (cost=0.57..528561.75 rows=68606 width=24) (actual time=85.472..2453.562 rows=31 loops=1)
                     Join Filter: ((topics.category_id = categories.id) AND ((categories.topic_id <> topics.id) OR (categories.id = 1
1)))
                     Rows Removed by Join Filter: 13938306
                     ->  Index Scan using index_topics_on_bumped_at on topics  (cost=0.42..100480.05 rows=715549 width=24) (actual ti
me=0.010..633.015 rows=464623 loops=1)
                           Filter: ((deleted_at IS NULL) AND ((archetype)::text <> 'private_message'::text))
                           Rows Removed by Filter: 105321
                     ->  Materialize  (cost=0.14..36.04 rows=30 width=8) (actual time=0.000..0.002 rows=30 loops=464623)
                           ->  Index Scan using categories_pkey on categories  (cost=0.14..35.89 rows=30 width=8) (actual time=0.006.
.0.040 rows=30 loops=1)
                                 Index Cond: (id = ANY ('{11,53,57,55,54,56,112,94,107,115,116,117,97,95,102,103,101,105,99,114,106,1
13,104,98,100,96,108,109,110,111}'::integer[]))
               ->  Index Scan using index_topic_users_on_topic_id_and_user_id on topic_users tu  (cost=0.43..0.53 rows=1 width=16) (a
ctual time=0.004..0.004 rows=0 loops=31)
                     Index Cond: ((topic_id = topics.id) AND (user_id = 1103877))
         ->  Materialize  (cost=0.28..2.30 rows=1 width=8) (actual time=0.000..0.000 rows=0 loops=30)
               ->  Index Scan using index_category_users_on_user_id_and_last_seen_at on category_users  (cost=0.28..2.29 rows=1 width
=8) (actual time=0.004..0.004 rows=0 loops=1)
                     Index Cond: (user_id = 1103877)
 Planning Time: 1.359 ms
 Execution Time: 2453.765 ms
(23 rows)
```

After:

```
                                                                                                                            QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=1.28..438.55 rows=30 width=12) (actual time=38.297..657.215 rows=30 loops=1)
   ->  Nested Loop Left Join  (cost=1.28..195944.68 rows=13443 width=12) (actual time=38.296..657.211 rows=30 loops=1)
         Filter: ((categories.topic_id <> topics.id) OR (topics.category_id = 11))
         Rows Removed by Filter: 29
         ->  Nested Loop Left Join  (cost=1.13..193462.59 rows=13443 width=16) (actual time=38.289..657.092 rows=59 loops=1)
               Join Filter: (category_users.category_id = topics.category_id)
               Filter: ((topics.category_id = 11) OR (COALESCE(category_users.notification_level, 1) <> 0) OR (tu.notification_level > 1))
               ->  Nested Loop Left Join  (cost=0.85..193156.79 rows=13489 width=20) (actual time=38.282..657.059 rows=59 loops=1)
                     Filter: ((COALESCE(tu.notification_level, 1) > 0) AND ((topics.category_id <> 11) OR (topics.pinned_at IS NULL) OR ((topics.pinned_at <= tu.cleared_pinned_at) AND (tu.cleared_pinned_at IS NOT NULL))))
                     Rows Removed by Filter: 1
                     ->  Index Scan using index_topics_on_bumped_at on topics  (cost=0.42..134521.06 rows=40470 width=24) (actual time=38.267..656.850 rows=60 loops=1)
                           Filter: ((deleted_at IS NULL) AND ((archetype)::text <> 'private_message'::text) AND (category_id = ANY ('{11,53,57,55,54,56,112,94,107,115,116,117,97,95,102,103,101,105,99,114,106,113,104,98,100,96,108,109,110,111}'::integer[])))
                           Rows Removed by Filter: 569895
                     ->  Index Scan using index_topic_users_on_topic_id_and_user_id on topic_users tu  (cost=0.43..1.43 rows=1 width=16) (actual time=0.003..0.003 rows=0 loops=60)
                           Index Cond: ((topic_id = topics.id) AND (user_id = 1103877))
               ->  Materialize  (cost=0.28..2.30 rows=1 width=8) (actual time=0.000..0.000 rows=0 loops=59)
                     ->  Index Scan using index_category_users_on_user_id_and_last_seen_at on category_users  (cost=0.28..2.29 rows=1 width=8) (actual time=0.004..0.004 rows=0 loops=1)
                           Index Cond: (user_id = 1103877)
         ->  Index Scan using categories_pkey on categories  (cost=0.14..0.17 rows=1 width=8) (actual time=0.001..0.001 rows=1 loops=59)
               Index Cond: (id = topics.category_id)
 Planning Time: 1.633 ms
 Execution Time: 657.255 ms
(22 rows)
```

* PERF: Optimize index on topics bumped_at.

Replace `index_topics_on_bumped_at` index with a partial index on `Topic#bumped_at` filtered by archetype since there is already another index that covers private topics.
2021-09-28 10:05:00 +08:00
..
auth FEATURE: Add global admin api key rate limiter (#12527) 2021-06-03 10:52:43 +01:00
common_passwords
concern FIX: Nil-filled CF arrays were not being deleted (#13518) 2021-06-25 11:34:51 +02:00
email FIX: Handle forwarded email quotes around Reply-To display name (#14384) 2021-09-20 16:26:18 +10:00
file_store FIX: Make sure S3 object headers are preserved on copy (#14302) 2021-09-10 12:59:51 +10:00
freedom_patches FIX: Ensure id sequences are not reset during db:migrate (#14184) 2021-08-30 12:31:22 +01:00
guardian FEATURE: Uppy direct S3 multipart uploads in composer (#14051) 2021-08-25 08:46:54 +10:00
highlight_js
imap FEATURE: Use group SMTP settings for sending user notification emails (initial) (#13220) 2021-06-03 14:47:32 +10:00
import
middleware FEATURE: Rate limit exceptions via ENV (#14033) 2021-08-13 12:00:23 -03:00
migration FIX: Allow post migrations using #change to carry out unsafe migration 2020-05-15 14:23:27 +08:00
plugin FEATURE: Add new plugin API to allow plugins to extend Site#categories (#13773) 2021-07-19 13:54:19 +08:00
pretty_text
rate_limiter
scheduler DEV: reduce logging when no external id is specified 2020-04-08 12:42:28 +10:00
site_settings DEV: Isolate multisite specs (#13634) 2021-07-07 18:57:42 +02:00
stylesheet FIX: Order outputted theme stylesheets (#14133) 2021-08-25 09:37:07 +08:00
svg_sprite FIX: Issues with custom icons in themes (#13732) 2021-07-14 15:18:29 -04:00
theme_store FEATURE: Allow themes to specify modifiers in their about.json file (#9097) 2020-03-11 13:30:45 +00:00
validators FEATURE: Humanize file size error messages (#14398) 2021-09-22 07:59:45 +10:00
wizard FEATURE: Enable auto dark mode on new instances (#14208) 2021-09-02 14:55:38 -04:00
admin_confirmation_spec.rb Update rubocop to 2.3.1. 2020-07-24 17:19:21 +08:00
admin_user_index_query_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
archetype_spec.rb
cache_spec.rb FIX: ensures defined expired_in is passed from write to write_entry (#11622) 2021-01-04 10:34:44 +01:00
category_badge_spec.rb
composer_messages_finder_spec.rb FEATURE: Make allow_uploaded_avatars accept TL (#14091) 2021-08-24 10:46:28 +03:00
content_buffer_spec.rb
cooked_post_processor_spec.rb FEATURE: Humanize file size error messages (#14398) 2021-09-22 07:59:45 +10:00
crawler_detection_spec.rb FEATURE: Implement browser update in crawler view (#12448) 2021-03-22 19:41:42 +02:00
current_user_spec.rb
directory_helper_spec.rb
discourse_diff_spec.rb Escape values of HTML attributes 2021-08-10 10:25:15 -04:00
discourse_event_spec.rb DEV: Plugin API to add directory columns (#13440) 2021-06-22 13:00:04 -05:00
discourse_hub_spec.rb
discourse_plugin_registry_spec.rb DEV: Remove deprecated plugins variables importer (#12168) 2021-02-23 16:20:59 -05:00
discourse_redis_spec.rb DEV: Remove specs that are no longer relevant. 2020-06-23 12:09:04 +08:00
discourse_spec.rb DEV: Improve Ember CLI's bootstrap logic (#12792) 2021-04-23 10:24:42 -04:00
discourse_tagging_spec.rb FIX: Show required tags to staff by default and override limit (#13242) 2021-06-02 12:43:34 -04:00
discourse_updates_spec.rb FIX: Sort admin dashboard new updates by latest (#12146) 2021-02-19 11:03:36 -05:00
distributed_memoizer_spec.rb DEV: s/\$redis/Discourse\.redis (#8431) 2019-12-03 10:05:53 +01:00
distributed_mutex_spec.rb DEV: Improve flaky time-sensitive specs (#9141) 2020-03-10 22:13:17 +01:00
email_cook_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
email_updater_spec.rb FEATURE: add maximum limit for secondary emails (#12599) 2021-04-05 20:31:42 +05:30
enum_spec.rb
excerpt_parser_spec.rb FIX: Make Oneboxer#apply insert block Oneboxes correctly (#11449) 2020-12-14 17:49:37 +02:00
feed_element_installer_spec.rb
feed_item_accessor_spec.rb
file_helper_spec.rb
filter_best_posts_spec.rb
final_destination_spec.rb FEATURE: Onebox can match engines based on the content_type (#13876) 2021-07-30 13:36:30 -04:00
flag_settings_spec.rb
gaps_spec.rb
global_path_spec.rb
guardian_spec.rb DEV: Remove the use of stubs. (#14142) 2021-08-25 13:25:01 +08:00
has_errors_spec.rb
hijack_spec.rb FIX: strip the trailing slash (/) of cors origins. (#10996) 2020-10-29 13:01:06 +11:00
html_prettify_spec.rb
html_to_markdown_spec.rb FIX: Hoisting linebreaks shouldn't fail for HTML5 elements (#14364) 2021-09-17 10:41:34 +02:00
image_sizer_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
inline_oneboxer_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
js_locale_helper_spec.rb FEATURE: Add English (UK) as locale (#11768) 2021-01-20 21:32:22 +01:00
json_error_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
letter_avatar_spec.rb
method_profiler_spec.rb
new_post_manager_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
new_post_result_spec.rb
oneboxer_spec.rb FEATURE: Censor Oneboxes (#12902) 2021-06-03 11:39:12 +10:00
onpdiff_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
pbkdf2_spec.rb
pinned_check_spec.rb
plain_text_to_markdown_spec.rb
post_action_creator_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
post_creator_spec.rb DEV: Ignore bookmarks.topic_id column and remove references to it in code (#14289) 2021-09-15 10:16:54 +10:00
post_destroyer_spec.rb DEV: Remove unncessary fabrication in tests. 2021-07-26 09:14:23 +08:00
post_locker_spec.rb
post_merger_spec.rb FIX: TL4 users cannot delete others posts (#13554) 2021-06-30 15:51:35 +03:00
post_revisor_spec.rb DEV: clarify the slow mode specs names (#13962) 2021-08-05 22:07:29 +04:00
presence_channel_spec.rb DEV: Introduce PresenceChannel API for core and plugin use 2021-08-27 16:26:06 +01:00
pretty_text_spec.rb FIX: Do not replace in mentions and hashtags (#14260) 2021-09-09 12:03:59 +03:00
promotion_spec.rb FIX: check if BasicBadge is enabled for TL1 welcome message (#13983) 2021-08-11 08:39:25 +10:00
quote_comparer_spec.rb
rate_limiter_spec.rb No need to disable rate limiter after running tests (#13093) 2021-05-19 16:04:35 +04:00
redis_store_spec.rb DEV: Implement a faster Discourse.cache 2019-11-27 16:11:49 +11:00
retrieve_title_spec.rb FIX: increase chunk size to fetch title tag correctly (#14144) 2021-09-03 13:15:58 +05:30
rtl_spec.rb
s3_helper_spec.rb FIX: Make sure S3 object headers are preserved on copy (#14302) 2021-09-10 12:59:51 +10:00
s3_inventory_multisite_spec.rb DEV: Isolate multisite specs (#13634) 2021-07-07 18:57:42 +02:00
s3_inventory_spec.rb DEV: Isolate multisite specs (#13634) 2021-07-07 18:57:42 +02:00
score_calculator_spec.rb
scss_checker_spec.rb PERF: Eager load Theme associations in Stylesheet Manager. 2021-06-21 11:06:58 +08:00
search_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
secure_session_spec.rb
site_icon_manager_spec.rb
site_setting_extension_multisite_spec.rb DEV: Isolate multisite specs (#13634) 2021-07-07 18:57:42 +02:00
site_setting_extension_spec.rb DEV: Isolate multisite specs (#13634) 2021-07-07 18:57:42 +02:00
slug_spec.rb DEV: Correct typos and spelling mistakes (#12812) 2021-05-21 11:43:47 +10:00
spam_handler_spec.rb FIX: use allowlist and blocklist terminology (#10209) 2020-07-27 10:23:54 +10:00
suggested_topics_builder_spec.rb
system_message_spec.rb FIX: TL2 promotion message and advance training (#10679) 2020-09-22 10:17:52 +10:00
text_cleaner_spec.rb FEATURE: Correctly convert topic title to uppercase and lowercase for Turkish default locale (#13115) 2021-05-24 18:13:30 +10:00
text_sentinel_spec.rb FIX: prevents exception when text input is nil (#12922) 2021-05-03 09:21:35 +02:00
theme_settings_manager_spec.rb DEV: use upload id to save in theme setting instead of URL. (#14341) 2021-09-16 07:58:53 +05:30
theme_settings_parser_spec.rb DEV: Don't user before(:all)/after(:all) (#13389) 2021-06-15 17:25:06 +02:00
timeline_lookup_spec.rb DEV: followup to 8edd2b38cb to use existing spec (#11830) 2021-01-25 12:04:27 +01:00
topic_creator_spec.rb FIX: Enforce tag group count validation before sending to review queue (#12728) 2021-04-19 09:43:50 +10:00
topic_publisher_spec.rb DEV: Improve flaky time-sensitive specs (#9141) 2020-03-10 22:13:17 +01:00
topic_query_spec.rb PERF: Improve database query perf when loading topics for a category. (#14416) 2021-09-28 10:05:00 +08:00
topic_retriever_spec.rb FEATURE: Stop checking referer for embeds (#13756) 2021-07-16 15:25:49 -03:00
topic_view_spec.rb FEATURE: Topic-level bookmarks (#14353) 2021-09-21 08:45:47 +10:00
topics_bulk_action_spec.rb FEATURE: Dismiss new and unread for PM inboxes. 2021-08-05 12:56:15 +08:00
trashable_spec.rb
trust_level_spec.rb
unread_spec.rb FEATURE: Add last visit indication to topic view page. (#13471) 2021-07-05 14:17:31 +08:00
url_helper_spec.rb FIX: errors loading secure uploads when secure uploads is disabled (#13047) 2021-06-08 13:25:51 -04:00
user_lookup_spec.rb REVERT "FIX: do not show private group flair on user avatars" (#13991) 2021-08-10 17:25:11 +05:30
user_name_suggester_spec.rb FIX: allow for final sigma in suggested usernames (#11540) 2020-12-23 08:51:36 +11:00
version_spec.rb DEV: Fix an apparently "too modern" git command (#10894) 2020-10-12 22:54:56 +02:00