PERF: improve performance of unread queries

Figuring out what unread topics a user has is a very expensive
operation over time.

Users can easily accumulate 10s of thousands of tracking state rows
(1 for every topic they ever visit)

When figuring out what a user has that is unread we need to join
the tracking state records to the topic table. This can very quickly
lead to cases where you need to scan through the entire topic table.

This commit optimises it so we always keep track of the "first" date
a user has unread topics. Then we can easily filter out all earlier
topics from the join.

We use pg functions, instead of nested queries here to assist the
planner.
This commit is contained in:
Sam
2017-05-25 15:07:12 -04:00
parent 6eb6c25816
commit 29fac1ac18
12 changed files with 226 additions and 38 deletions

View File

@@ -81,6 +81,30 @@ class UserStat < ActiveRecord::Base
update_columns(reset_bounce_score_after: nil, bounce_score: 0)
end
def self.update_first_topic_unread_at!
exec_sql <<SQL
UPDATE user_stats us
SET first_topic_unread_at = COALESCE(X.first_unread_at, 'epoch')
FROM
(
SELECT u.id user_id, MIN(last_unread_at) first_unread_at
FROM users u
JOIN topic_users tu ON tu.user_id = u.id
JOIN topics t ON t.id = tu.topic_id
WHERE notification_level > 1 AND last_read_post_number < CASE WHEN moderator OR admin
THEN t.highest_staff_post_number
ELSE t.highest_post_number
END
AND t.deleted_at IS NULL AND t.archetype <> 'private_message'
GROUP BY u.id
) X
WHERE X.user_id = us.user_id AND X.first_unread_at <> first_topic_unread_at
SQL
nil
end
protected
def trigger_badges